gpt4 book ai didi

javascript - d3访问分组条形图中的嵌套数据

转载 作者:可可西里 更新时间:2023-11-01 02:43:47 24 4
gpt4 key购买 nike

我正在通过嵌套 .csv 文件构建分组条形图。该图表也可以作为折线图查看,因此我想要一个适合折线对象的嵌套结构。我原来的 .csv 看起来像这样:

Month,Actual,Forecast,Budget
Jul-14,200000,-,74073.86651
Aug-14,198426.57,-,155530.2499
Sep-14,290681.62,-,220881.4631
Oct-14,362974.9,-,314506.6437
Nov-14,397662.09,-,382407.67
Dec-14,512434.27,-,442192.1932
Jan-15,511470.25,511470.25,495847.6137
Feb-15,-,536472.5467,520849.9105
Mar-15,-,612579.9047,596957.2684
Apr-15,-,680936.5086,465313.8723
May-15,-,755526.7173,739904.081
Jun-15,-,811512.772,895890.1357

我的嵌套是这样的:

  d3.csv("data/net.csv", function(error, data) {
if (error) throw error;

var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});

data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {

return {
name: name, // "name": the csv headers except month
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};

});

构建图表的代码是:

  var bars = svg.selectAll(".barGroup")
.data(data) // Select nested data and append to new svg group elements
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });

bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name); });

g 元素很好,各个条形图被映射到它们,正确应用了 x 值和类。

我的问题在于访问条形的高度和 y 值的“速率”数据。在上面的表格中,它给出了一个 NaN。我还尝试使用类别数据附加 g 元素,然后附加 rects:

  .data(function(d) { return d.values })

这允许我访问速率数据,但将所有 36 个柱映射到每个 rangeBand。

它在更扁平的数据结构中也能正常工作,但当它向下嵌套两层时我似乎无法使用它,尽管查看了大量示例和 SO 问题。

如何访问费率数据?

应Cyril的要求,完整代码如下:

    var margin = {top: 20, right: 18, bottom: 80, left: 50},
w = parseInt(d3.select("#bill").style("width")) - margin.left - margin.right,
h = parseInt(d3.select("#bill").style("height")) - margin.top - margin.bottom;

var customTimeFormat = d3.time.format.multi([
[".%L", function(d) { return d.getMilliseconds(); }],
[":%S", function(d) { return d.getSeconds(); }],
["%I:%M", function(d) { return d.getMinutes(); }],
["%I %p", function(d) { return d.getHours(); }],
["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
["%b %d", function(d) { return d.getDate() != 1; }],
["%b", function(d) { return d.getMonth(); }],
["%Y", function() { return true; }]
]);


var parseDate = d3.time.format("%b-%y").parse;

var displayDate = d3.time.format("%b %Y");

var xScale = d3.scale.ordinal()
.rangeRoundBands([0, w], .1);

var xScale1 = d3.scale.linear()
.domain([0, 2]);

var yScale = d3.scale.linear()
.range([h, 0])
.nice();

var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(customTimeFormat)
.orient("bottom");

var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.innerTickSize(-w)
.outerTickSize(0);

var svg = d3.select("#svgCont")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var thous = d3.format(",.0f")

var lineClass = d3.scale.ordinal().range(["actual", "forecast", "budget"]);

var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<p id='date'>" + displayDate(d.date) + "</p><p id='value'>$" + thous(d.rate);
})

d3.csv("data/net.csv", function(error, data) {
if (error) throw error;

var headers = d3.keys(data[0]).filter(function(head) {
return head != "Month";
});

data.forEach(function(d) {
d.month = parseDate(d.Month);
});
var categories = headers.map(function(name) {

return {
name: name,
values: data.map(function(d) {
return {
date: d.month,
rate: +(d[name]),
};
}),
};

});

var min = d3.min(categories, function(d) {
return d3.min(d.values, function(d) {
return d.rate;
});
});



var max = d3.max(categories, function(d) {
return d3.max(d.values, function(d) {
return d.rate;
});
});

var minY = min < 0 ? min * 1.2 : min * 0.8;

xScale.domain(data.map(function(d) { return d.month; }));
yScale.domain([minY, (max * 1.1)]);

var barWidth = headers.length > 2 ? xScale.rangeBand() / 2 : xScale.rangeBand() ;

svg.call(tip);

svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);

svg.append("g")
.attr("class", "y axis")
.call(yAxis);

var bars = svg.selectAll(".barGroup")
.data(data)
.enter()
.append("g")
.attr("class", "barGroup")
.attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; });

bars.selectAll("rect")
.data(categories)
.enter()
.append("rect")
.attr("width", barWidth)
.attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand() / 2;}})
.attr("y", function (d) { return yScale(d.rate); })
.attr("height", function (d) { return h - yScale(d.rate); })
.attr("class", function (d) { return lineClass(d.name) + " bar"; });


var legend = svg.selectAll(".legend")
.data(headers)
.enter()
.append("g")
.attr("class", "legend");

legend.append("line")
.attr("class", function(d) { return lineClass(d); })
.attr("x1", 0)
.attr("x2", 40)
.attr("y1", function(d, i) { return (h + 30) + (i *14); })
.attr("y2", function(d, i) { return (h + 30) + (i *14); });

legend.append("text")
.attr("x", 50)
.attr("y", function(d, i) { return (h + 32) + (i *14); })
.text(function(d) { return d; });

svg.selectAll(".bar")
.on('mouseover', tip.show)
.on('mouseout', tip.hide);

});

2016 年 2 月 18 日更新。

看来我还没有充分解释我想做的事情。图表的折线图和条形图版本将分开显示,即用户可以根据对选择元素的输入来查看任一版本。另请注意,我无法控制数据最初的输入方式。

我有a version of exactly how it should work here.

这个问题是在我还在研究它时提出的,但我从未解决过这个问题——我使用了一种解决方法,即对数据进行两个单独的嵌套。

最佳答案

链接到 jsfiddle: https://jsfiddle.net/sladav/rLh4qwyf/1/

我认为问题的根源在于您想要使用原始数据集中未明确存在的两个变量:(1) 类别(2) 比率 .

您的数据采用宽格式格式化,因为每个类别都有自己的变量,并且 rate 的值存在于月份和给定类别之一的十字路口。我认为你嵌套的方式最终是或者至少应该解决这个问题,但我不清楚翻译中是否或在哪里丢失了某些东西。从概念上讲,我认为从一个与你想要完成的目标相匹配的组织开始更有意义。我重新格式化了原始数据并再次处理它 - 在概念层面上,嵌套看起来简单明了......

新专栏:

  • 月:时间变量;映射到X轴
  • 类别:分类值 [实际、预测、预算];用于分组/着色
  • 费率:数值;映射到Y轴

重组的 CSV(删除 NULL):

Month,Category,Rate
Jul-14,Actual,200000
Aug-14,Actual,198426.57
Sep-14,Actual,290681.62
Oct-14,Actual,362974.9
Nov-14,Actual,397662.09
Dec-14,Actual,512434.27
Jan-15,Actual,511470.25
Jan-15,Forecast,511470.25
Feb-15,Forecast,536472.5467
Mar-15,Forecast,612579.9047
Apr-15,Forecast,680936.5086
May-15,Forecast,755526.7173
Jun-15,Forecast,811512.772
Jul-14,Budget,74073.86651
Aug-14,Budget,155530.2499
Sep-14,Budget,220881.4631
Oct-14,Budget,314506.6437
Nov-14,Budget,382407.67
Dec-14,Budget,442192.1932
Jan-15,Budget,495847.6137
Feb-15,Budget,520849.9105
Mar-15,Budget,596957.2684
Apr-15,Budget,465313.8723
May-15,Budget,739904.081
Jun-15,Budget,895890.1357

对于新格式化的数据,您首先使用 d3.nest 通过 CATEGORY 变量明确地对数据进行分组。现在您的数据存在于两层中。第一层有三个组(每个类别一个)。第二层包含每条线/每组柱的汇率数据。您还必须嵌套数据选择 - 第一层用于绘制线条,第二层用于绘制条形。

嵌套数据:

var nestedData = d3.nest()
.key(function(d) { return d.Category;})
.entries(data)

为分组的第一层数据创建 svg 组:

d3.select(".plot-space").selectAll(".g-category")
.data(nestedData)
.enter().append("g")
.attr("class", "g-category")

使用此数据添加您的线路/路径:

d3.selectAll(".g-category").append("path")
.attr("class", "line")
.attr("d", function(d){ return lineFunction(d.values);})
.style("stroke", function(d) {return color(d.key);})

最后,“进入”第二层以添加条形/矩形:

d3.selectAll(".g-category").selectAll(".bars")
.data(function(d) {return d.values;})
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {return x(d.Month);})
.attr("y", function(d) {return y(d.Rate);})
.attr("width", 20)
.attr("height", function(d) {return height - y(d.Rate)})
.attr("fill", function(d) {return color(d.Category)})

这是一种直接的方法(至少对我而言),因为您一次选择一个类别,使用分组数据绘制一条线,然后使用单个数据点绘制条形。

惰性编辑:

并排显示类别栏

创建序号尺度映射类别到 [1,nCategories]。使用它来动态偏移像

这样的条
translate( newScale(category)*barWidth )

显示条形或线条(不是两者)

创建一个函数来选择条/线和过渡/切换它们的可见性/不透明度。当您的下拉输入更改时运行,并将下拉输入作为函数的输入。

关于javascript - d3访问分组条形图中的嵌套数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34302958/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com