带有JSON嵌套数据的D3 Grouped Bar Chart

时间:2014-01-25 01:39:33

标签: json d3.js

我正在尝试创建一个类似的分组条形图,如下例所示:http://bl.ocks.org/mbostock/3887051我的数据是来自MongoDB的JSON,我无法访问某些嵌套数据。我希望图表在Y轴上由buildFixTime设置,并且X轴是按时间排列的,并且条形图按“组”分组。这是一个示例记录:

{
        "result" : [
                {
                        "_id" : {
                                "month" : 1,
                                "day" : 22,
                                "year" : 2014,
                                "group" : "Sales"
                        },
                        "buildFixTime" : 3710497
                },
                {
                        "_id" : {
                                "month" : 1,
                                "day" : 23,
                                "year" : 2014,
                                "group" : "Sales"
                        },
                        "buildFixTime" : 79209205
                },
                {
                        "_id" : {
                                "month" : 1,
                                "day" : 24,
                                "year" : 2014,
                                "group" : "Sales"
                        },
                        "buildFixTime" : 35611663.4
                },
                {
                        "_id" : {
                                "month" : 1,
                                "day" : 24,
                                "year" : 2014,
                                "group" : "Service"
                        },
                        "buildFixTime" : 18273221.333333332
                }
        ],
        "ok" : 1
}

加载JSON文件后,我有以下代码(不包括不重要的位):

// This adds new elements to the data object
data.result.forEach(function(d) {
  d.group = d._id.group;
  d.date = new Date(d._id.year, d._id.month-1, d._id.day);
});

// define the axis domains
main_x0.domain(d3.extent(data.result, function(d) { return d.date; }));
main_x1.domain(d3.ascending(data.result, function(d) { return d.group; } ));
main_y.domain(d3.extent(data.result, function(d) { return (d.buildFixTime / 1000) / 60 / 60 + 2 ; }));

// flatten out the data
var nested = d3.nest()
    .key(function(d) { return d._id.group; })
    .entries(data.result);

添加轴线等后,我有这个块,这就是问题所在。我认为我需要能够在转换/转换行中访问日期,但我不知道如何实现它 - 我应该创建另一个D3嵌套对象来进一步展平数据吗?谢谢你的帮助!

var bar = main.selectAll(".bars")
    .data(nested)
  .enter().append("g")
    .attr("class", "g")

    // This seems to be where part of my problem is
    .attr("transform", function(d) { console.log(d); return "translate(" + main_x0(d.values.date) + ",0)"; });

bar.selectAll("rect").append("rect")
    .data(function(d) { return d.values; })
  .enter().append("rect")
    .attr("transform", function(d) { console.log(d.date); return "translate(" + main_x0(d.date) + ",0)"; })
    .attr("width", main_x1.rangeBand())
    .attr("x", function(d) { return main_x1(d.date); })
    .attr("y", function(d) { return main_y(d.buildFixTime); })
    .attr("height", function(d) { return main_height - main_y(d.buildFixTime); })
    .style("fill", function(d) { return color(d.key); });

1 个答案:

答案 0 :(得分:6)

你似乎对你的两个x刻度,每个人做什么以及何时使用它们感到有点困惑。

使用链接示例中的变量名称, x0比例会在页面上显示不同的条形群

我并不完全清楚你的意思是“我希望X轴按时间排列,并且条形按'组'分组。”我假设你想要所有的“组”每个月聚集在一起,不同的月份分布在整个页面上。如果您希望将每个“组”的所有日期聚集在一起,那么x0的域应该是您的,而不是您的日期,并且您将不得不调整本讨论的其余部分

因为日期可以被视为连续数字,所以您可以使用线性比例;通过传递数据的extent(即最大值和最小值)而不是所有可能值的排序列表,看起来就像你正在做的那样。但是,线性刻度并不是一种简单的方法来告诉您每个数据点之间有多少空间,我们需要这些空间来定位和调整条形。因此,为了使一切变得更容易,请将其设置为序数,并将其域设置为已排序的日期。

x1比例相对于该群集的起始位置,将群集中的各个条形隔开。因此,域名是您希望在每个月列出的“群组”。

看起来你没有为这个音阶设定范围。 x1比例的范围是每个聚类可用的宽度,由x0比例的带宽决定:如果你有很多聚类,每个聚类将更窄,所以单个条也必须更窄

所以你的轴设置是(假设簇之间有20%的填充,而且各个条之间没有填充):

// initialization
main_x0 = d3.scale.ordinal().rangeRoundBands([0, main_width], 0.2); 
main_x1 = d3.scale.ordinal();
main_y  = d3.scale.linear().range([main_height, 0] );

// once you have the data
main_x0.domain(data.result.map( function(d) { return d.date; } )
                          .sort(d3.ascending) 
               );

main_x1.domain(data.result.map( function(d) { return d.group; } )
                          .sort(d3.ascending) 
               )
       .rangeRoundBands([0, main_x0.rangeBand() ], 0);

main_y.domain(d3.extent(data.result, function(d) { return d.buildFixTime ; }));

(PS我不知道为什么你的y范围域上有各种各样的转换方程式。你在绘制数据时只使用原始buildFixTime,这就是你想要的您的域名。应在tickFormat函数中完成从毫秒到小时的转换。

然后,在您的图表方法中,您需要记住main_x0是您的日期群集比例,而main_x1是您在每个日期内的规模。每个条的水平位置通过首先将其移动到簇的开始然后再将其移动到该组在簇内的位置来确定。应用于日期的x0比例为您提供第一个班次,而应用于该组的x1比例则为您提供第二个班次。

换句话说,如果你想要这样的布局:

abcde  abcde  abcde  abcde
 Jan.   Feb.   Mar.   Apr.  
       *             
       >>>>|

然后要找到二月份类别“e”的位置,您需要x0(二月)+ x1(e)。第一个为您提供二月集群的开头(标记为*),第二个为您提供了进入类别e(标有>)的额外转移。

您的数据嵌套在“group”中。因此,您的每个<g>元素都包含特定类别中的所有条形:一个中的所有a,另一个中的所有b。虽然可以使用<g>元素上的变换来根据每个群集中所需的群组移动来移动每个群组的起点,但我认为这会使事情变得复杂。正如Lars所说,你可以在定位单个酒吧时完成所有工作。

所以你的代码看起来像是:

var bar = main.selectAll(".bars")
    .data(nested)
  .enter().append("g")
    .attr("class", function(d){return d.key;})
    //add a useful class based on the data
    //d.key is the value used to create the nested array
    //i.e., the group value for all the bars in the <g>

    .style("fill", function(d) { return color(d.key); });
    //Set the style here and it will be inherited by the bars
    //in the group. If you set it on the individual rectangles, 
    //you will need to use d.group (the data property), 
    //not d.key (the property created by the nest method) 

bar.selectAll("rect").append("rect")
    .data(function(d) { return d.values; })
     //the nested sub-array for each group

  .enter().append("rect")
    .attr("transform", function(d) {
          return "translate(" + main_x0(d.date) + ",0)"; 
     })
    .attr("x", function(d) { return main_x1(d.group); })
    //by using both a transform and an x-position, the two scales
    //are kept separate.  But make sure that you pass in the correct
    //data variable for each scale!  

    .attr("width", main_x1.rangeBand())
    //the width is calculated from the x1 scale, which positions
    //the individual bars within a cluster

    .attr("y", function(d) { return main_y(d.buildFixTime); })
    .attr("height", function(d) { 
             return main_height - main_y(d.buildFixTime); 
     });
    //note that these functions are based on a y-scale with an inverted range, 
    //one that goes from [main_height, 0], as I set up above

同样,如果您希望将条形图分组,则必须将所有x0缩放函数切换为使用d.group,并将所有x1缩放函数切换为d.date。< / p>