D3:显示第一个月和一月的年份,并将刻度移动到小节中点 [英] D3: show years on first month and on January, and move ticks to bar midpoints

查看:47
本文介绍了D3:显示第一个月和一月的年份,并将刻度移动到小节中点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下内容:

page.html :

<!DOCTYPE html>
<!-- https://bl.ocks.org/mbostock/3886208 -->
<style>

</style>
<script src="https://d3js.org/d3.v4.js"></script>
<body>

<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 1300 - margin.left - margin.right,
    height = 700 - margin.top - margin.bottom;

var svg = d3.select("body").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom);

var g = svg.append("g")
           .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// parse the date / time
// look at the .csv in Notepad! DO NOT LOOK AT EXCEL!
var parseDate = d3.timeParse("%m/%d/%Y");


var x = d3.scaleTime()
          .range([0, width - margin.left - margin.right]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal()
          .range(["#CE1126", "#00B6D0"]); // red and blue 

var xAxis = d3.axisBottom(x)
              .ticks(d3.timeMonth.every(1))
              .tickFormat(d3.timeFormat("%b")); // label every month

// load .csv file
d3.csv("test_data.csv", function(d, i, columns) {
  for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
  d.total = t;
  return d;
}, function(error, data){
    if (error) throw error;

    data.forEach(function(d) {
        //console.log(parseDate(d.date));
        d.date = parseDate(d.date);
    });

    var keys = data.columns.slice(1);

    data.sort(function(a, b) { return b.date - a.date; });
    x.domain(d3.extent( data, function(d){ return d.date }) );
    y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
    z.domain(keys);

    //console.log(d3.stack().keys(keys)(data));

    // the bars 
    g.append("g")
     .selectAll("g")
     .data(d3.stack().keys(keys)(data))
     .enter().append("g")
     .attr("fill", function(d) { return z(d.key); })
     .selectAll("rect")
     .data(function(d) { return d; })
     .enter()
     .append("rect")
     .attr("x", function(d) { return x(d.data.date); })
     .attr("y", function(d) { return y(d[1]); })
     .attr("height", function(d) { return y(d[0]) - y(d[1]); })
     .attr("width", (width - margin.right- margin.left)/data.length );

    // x-axis
    g.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);


});
</script>

</body>

test_data.csv :

date,col_1,col_2
11/1/2012,1977652,1802851
12/1/2012,1128739,948687
1/1/2013,1201944,1514667
2/1/2013,1863148,1834006
3/1/2013,1314851,1906060
4/1/2013,1283943,1978702
5/1/2013,1127964,1195606
6/1/2013,1773254,977214
7/1/2013,1929574,1127450
8/1/2013,1980411,1808161
9/1/2013,1405691,1182788
10/1/2013,1336790,937890
11/1/2013,1851053,1358400
12/1/2013,1472623,1214610
1/1/2014,1155116,1757052
2/1/2014,1571611,1935038
3/1/2014,1898348,1320348
4/1/2014,1444838,1934789
5/1/2014,1235087,950194
6/1/2014,1272040,1580656
7/1/2014,980781,1680164
8/1/2014,1391291,1115999
9/1/2014,1211125,1542148
10/1/2014,1020824,1782795
11/1/2014,1685081,926612
12/1/2014,1469254,1767071
1/1/2015,1168523,935897
2/1/2015,1602610,1450541
3/1/2015,1830278,1354876
4/1/2015,1275158,1412555
5/1/2015,1560961,1839718
6/1/2015,949948,1587130
7/1/2015,1413765,1494446
8/1/2015,1166141,1305105
9/1/2015,958975,1202219
10/1/2015,902696,1023987
11/1/2015,961441,1865628
12/1/2015,1363145,1954046
1/1/2016,1862878,1470741
2/1/2016,1723891,1042760
3/1/2016,1906747,1169012
4/1/2016,1963364,1927063
5/1/2016,1899735,1936915
6/1/2016,1300369,1430697
7/1/2016,1777108,1401210
8/1/2016,1597045,1566763
9/1/2016,1558287,1140057
10/1/2016,1965665,1953595
11/1/2016,1800438,937551
12/1/2016,1689152,1221895
1/1/2017,1607824,1963282
2/1/2017,1878431,1415658
3/1/2017,1730296,1947106
4/1/2017,1956756,1696780
5/1/2017,1746673,1662892
6/1/2017,989702,1537646
7/1/2017,1098812,1592064
8/1/2017,1861973,1892987
9/1/2017,1129596,1406514
10/1/2017,1528632,1725020
11/1/2017,925850,1795575

我们获得以下输出:

我想做两件事:

  1. 将轴稍微向右移动,以使每个月的刻度线位于每个小节的中点.轴仍应从第一个小节的起点延伸到最后一个小节的终点;刻度线刚刚移动.
  2. 在第一次打勾的月份标签下(在这种情况下,2012年11月)并在每次出现"Jan"的情况下显示年份的值.

我不一定需要详细的解决方案;只知道在哪里看会很棒.

推荐答案

您面临的关键问题是您的x域不能反映您绘制数据的方式.由于您将条形图绘制为一个月宽,因此您的域会在结束点后再延长一个月(它不会在11月1日结束,而是在11月30日结束).这就是为什么未绘制轴的末端部分(域线)的原因.

The key issue you face is that your x domain does not reflect how you draw your data. Because you draw your bars as being one month wide, your domain extends one extra month past your end point (it doesn't end November 1, it ends November 30). This is why the end part of your axis (the domain line) is not drawn.

以另一种方式输入,域中的最大值位于绘图区域的边缘.如果仔细看,您可能会注意到最后一个条形图被绘制在边距中-它从绘制区域的最大svg x值开始延伸,并因此延伸超过了轴.

Put another way, the maximum value in the domain is at the edge of the plot area. If you look carefully, you might notice that the last bar is plotted in the margin - it extends from the maximum svg x value of the plot area, and consequently, extends past the axis.

这是在使用带刻度的时间刻度时遇到的主要挑战,轴上的任何点都只是一个点-尽管您将这些点表示为具有宽度-而且刻度仅从起点延伸到终点.如果要保留时间刻度,则需要扩展域以考虑最后一个小节的宽度.由于您的数据点代表几个月,因此您需要将x标度域扩展一个月:

This is a key challenge in using a time scale with bars, any point on the axis is merely a point - while you represent these points as having width - and your scale only extends from the start point to the end point. If you want to keep a time scale, you need to extend your domain to account for the last bar's width. Since your data points represent months, you need to extend your x scale domain by one month:

x.domain(d3.extent( data, function(d){ return d.date }) );

var max = x.domain()[1]; // get the maximum date in the domain
var min = x.domain()[0]; // get the minimum date in the domain

var newMax = new Date(); 

newMax.setTime(max.getTime() + 29.99 * 86400000); // set a new max date for the end of November, see Gerardo's comment below for a better way to calculate dates. However, adding a full month may add a new month label to the chart. Removing it though, isn't that difficult.

x.domain([min,newMax]) // set the new domain.

此逻辑可能会根据您终止的月份中的天数而改变.

您还应该相应地更改条形宽度:

You should also change the bar width accordingly:

var barWidth = (width - margin.right- margin.left)/(data.length+1)

现在,您需要调整刻度线.刻度线在刻度上标记了一个时间点,当您将条形图放置在基于该点的x位置时,您需要将刻度线向右移动条形图宽度的一半.您可以这样做:

Now you need to adjust the ticks. The tick marks a point in time on the scale, and as you place your bars to have their x position based on that point, you need to move the ticks to the right by half the bars' width. You can do so with:

var axis = g.append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + height + ")")
  .call(xAxis)

axis.selectAll("g").select("text")
  .attr("transform","translate(" + ( (width - margin.right- margin.left)/(data.length+1)/2 ) + ",0)");

这应该给你类似的东西

请参见 bl.ock .

我将刻度线留在了原来的位置,如果需要,您也可以翻译它们,但是您需要删除第一个或最后一个,因为您需要的刻度线要比条形线少.

I've left the ticks lines where they were, you can translate them as well if you want, but you will need to remove either the first or last one as you need one less tick than you have bars.

您在这里要问两个不同的问题,因此,我将提供一些建议,以显示年份,使用第二个x轴,每年显示刻度,并设置格式以显示年份(与几个月相同).只需确保将其放置在较低的位置即可(您可能需要在底部留出更多的边距).删除域行并删除(或格式化)刻度线.

You are asking two different questions here, so I'll offer a quick suggestion for showing the years, use a second x axis, show ticks every year and format it to show the year (same as you do for months). Just be sure to position it lower (you might need more margin on the bottom). Remove the domain line and remove (or format) the tick lines.

这篇关于D3:显示第一个月和一月的年份,并将刻度移动到小节中点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆