[英]d3 v5 use nested values when scaling axis
我有以下代码根据地区和日期嵌套我的数据。 我遇到的问题是我不知道如何定义 yScale 来动态绘制轴,以便返回嵌套数据的最大总和(嵌套数据的 max val 高于数据集 bc 中的 max val它是聚合的)。 因此,我的 yAxis 被截断,图表没有显示所有数据。
在代码中,如果我将域硬编码为 to.domain([0, 3500]) 那么轴是正确的,否则它是不正确的。 我不想对域进行硬编码。 如何引用嵌套值?
已编辑以显示注释中提供的代码,这有助于但不能完全修复脚本在整个数据集上运行时的问题。 请看底部的图片。
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {
return parseInt(d.count,10);
})])
.range([h - padding, padding])
// var yScale = d3.scaleLinear()
// .domain([0, 3500])
// .range([h - padding, padding]) //not supposed to hard code the scale but it is not working
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Nested Chart</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
.pagebreak {
page-break-before: always;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.point {
fill: none;
size: 2px
}
.dot {
fill: #ffab00;
stroke: #fff;
}
</style>
</head>
<div style="width:800px; margin:0 auto;" class='body'></div>
<div class="pagebreak"> </div>
<body>
<script type="text/javascript">
var parseTime = d3.timeParse("%Y");
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var padding = 20;
/////////////////get the data//////////////////
const csv = `state,region,year,count
Alabama,South,2010,1
Alabama,South,2011,1
Alabama,South,2012,0
Alabama,South,2013,0
Alabama,South,2014,2
Alabama,South,2015,6
Alaska,West,2010,2245
Alaska,West,2011,1409
Alaska,West,2012,1166
Alaska,West,2013,1329
Alaska,West,2014,1296
Alaska,West,2015,1575
Connecticut,Northeast,2010,0
Connecticut,Northeast,2011,0
Connecticut,Northeast,2012,0
Connecticut,Northeast,2013,0
Connecticut,Northeast,2014,0
Connecticut,Northeast,2015,1
Missouri,Midwest,2010,2
Missouri,Midwest,2011,3
Missouri,Midwest,2012,2
Missouri,Midwest,2013,0
Missouri,Midwest,2014,1
Missouri,Midwest,2015,5
California,West,2010,546
California,West,2012,243
California,West,2013,240
Wyoming,West,2015,198
California,West,2011,195
California,West,2014,191`;
const dataset = d3.csvParse(csv);
dataset.forEach(function(d) {
d.date = parseTime(d.year);
d.region = d['region'];
d.state = d['state'];
d.count = d['count'];
//console.log(d)
});
/////////////////scales the data//////////////////
var xScale = d3.scaleTime()
.domain([d3.min(dataset, function(d) {
return d.date
}), d3.max(dataset, function(d) {
return d.date
})]).range([padding, w - padding * 2])
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {
console.log(d.count)
return d.count ///ERROR HERE
})]).range([h - padding, padding])
// var yScale = d3.scaleLinear()
// .domain([0, 3500])
// .range([h - padding, padding]) //not supposed to hard code the scale but it is not working otherwise...commented out above
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
/////////////////charts start here//////////////////
var svg = d3.select("body").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var svg1 = d3.select("body").append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//Define the line
var valueLine = d3.line()
.x(function(d) {
return xScale(new Date(d.key));
})
.y(function(d) {
return yScale(d.value);
})
var nest = d3.nest()
.key(function(d) {
return d.region;
})
.key(function(d) {
return d.date;
})
.rollup(function(leaves) {
return d3.sum(leaves, function(d) {
return (d.count)
});
})
.entries(dataset)
console.log(nest)
// Set the color scheme
var colors = d3.scaleOrdinal()
.domain(["South", "West", "Northeast","Midwest"])
.range(["#EF5285", "#88F284" , "#5965A3","#900C3F"]);
var regYear = svg.selectAll(".regYear")
.data(nest)
.enter()
.append("g")
.attr("stroke", function(d){ return colors(d.key)}); // Adding color!
// console.log(regYear)
var paths = regYear.selectAll(".line")
.data(function(d) {
return [d.values]
})
.enter()
.append("path");
// Draw the line
paths
.attr("d", function(d) {
return valueLine(d)
})
.attr("class", "line")
.style("fill", "none");
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.attr("class", "dot") // Assign a class for styling
.attr("cx", function(d, i) { return xScale(i) })
.attr("cy", function(d) { return yScale(d.count) })//this is not working
.attr("r", 5);
svg.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
//draw Y axis
svg.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
// add label
svg.append("text").attr("x", (w / 2)).attr("y", h + 30).attr("text-anchor", "middle").text("Year");
svg.append("text").attr("x", padding).attr("y", padding - 20).attr("text-anchor", "middle").text("# of Events");
//add title
svg.append("text").attr("x", (w / 2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("x", w - 65)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100);
////////////////////////////////////END///////////////////////////
</script>
</body>
</html>
更新:
yAxis 的最大值小于nest
数据的实际最大值,因此被截断。
您必须在 yScale 计算中使用nest
数据,而不是使用原始dataset
数据。
实现这一目标的步骤:
nest
nest
获得totalMax
值totalMax
计算yScale
值var totalMax = Object
.entries(nest)
.reduce(function(totalMax, [key, regionValue]){
const regionMax = Object
.entries(regionValue.values)
.reduce(function(regionMax, [key,yearValue]){
return parseInt(yearValue.value,10) > regionMax ? parseInt(yearValue.value, 10) : regionMax;
}, 0)
return parseInt(regionMax, 10) > totalMax ? parseInt(regionMax, 10) : totalMax;
}, 0)
var yScale = d3.scaleLinear().domain([0, totalMax]).range([h - padding, padding])
我根据您的代码编写了一个 Demo:
var parseTime = d3.timeParse("%Y"); var margin = { top: 20, right: 20, bottom: 30, left: 50 }, w = 960 - margin.left - margin.right, h = 500 - margin.top - margin.bottom; var padding = 20; /////////////////get the data////////////////// const csv = `state,region,year,count Alabama,South,2010,1 Alabama,South,2011,1 Alabama,South,2012,0 Alabama,South,2013,0 Alabama,South,2014,2 Alabama,South,2015,6 Alaska,West,2010,2245 Alaska,West,2011,1409 Alaska,West,2012,1166 Alaska,West,2013,1329 Alaska,West,2014,1296 Alaska,West,2015,1575 Connecticut,Northeast,2010,0 Connecticut,Northeast,2011,0 Connecticut,Northeast,2012,0 Connecticut,Northeast,2013,0 Connecticut,Northeast,2014,0 Connecticut,Northeast,2015,1 Missouri,Midwest,2010,2 Missouri,Midwest,2011,3 Missouri,Midwest,2012,2 Missouri,Midwest,2013,0 Missouri,Midwest,2014,1 Missouri,Midwest,2015,5 California,West,2010,546 California,West,2012,243 California,West,2013,240 Wyoming,West,2015,198 California,West,2011,195 California,West,2014,191`; const dataset = d3.csvParse(csv); dataset.forEach(function(d) { d.date = parseTime(d.year); d.region = d['region']; d.state = d['state']; d.count = d['count']; }); var nest = d3.nest().key(function(d) { return d.region; }).key(function(d) { return d.date; }).rollup(function(leaves) { return d3.sum(leaves, function(d) { return parseInt(d.count, 10); }); }).entries(dataset) /////////////////scales the data////////////////// var xScale = d3.scaleTime().domain([d3.min(dataset, function(d) { return d.date }), d3.max(dataset, function(d) { return d.date })]).range([padding, w - padding * 2]) var totalMax = Object.entries(nest).reduce(function(totalMax, [key, regionValue]){ const regionMax = Object.entries(regionValue.values).reduce(function(regionMax, [key,yearValue]){ return parseInt(yearValue.value,10) > regionMax? parseInt(yearValue.value, 10): regionMax; }, 0) return parseInt(regionMax, 10) > totalMax? parseInt(regionMax, 10): totalMax; }, 0) var yScale = d3.scaleLinear().domain([0, totalMax]).range([h - padding, padding]) // var yScale = d3.scaleLinear() //.domain([0, 3500]) //.range([h - padding, padding]) //not supposed to hard code the scale but it is not working otherwise...commented out above var xAxis = d3.axisBottom().scale(xScale); var yAxis = d3.axisLeft().scale(yScale); /////////////////charts start here////////////////// var svg = d3.select("body").append("svg").attr("width", w + margin.left + margin.right).attr("height", h + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var svg1 = d3.select("body").append("svg").attr("width", w + margin.left + margin.right).attr("height", h + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); //Define the line var valueLine = d3.line().x(function(d) { return xScale(new Date(d.key)); }).y(function(d) { return yScale(d.value); }) // Set the color scheme var colors = d3.scaleOrdinal().domain(["South", "West", "Northeast","Midwest"]).range(["#EF5285", "#88F284", "#5965A3","#900C3F"]); var regYear = svg.selectAll(".regYear").data(nest).enter().append("g").attr("stroke", function(d){ return colors(d.key)}); // Adding color. var paths = regYear.selectAll(".line").data(function(d) { return [d.values] }).enter();append("path"). // Draw the line paths,attr("d". function(d) { return valueLine(d) }),attr("class". "line"),style("fill"; "none"). svg.selectAll(".dot").data(dataset).enter().append("circle") // Uses the enter().append() method,attr("class". "dot") // Assign a class for styling,attr("cx", function(d. i) { return xScale(i) }),attr("cy". function(d) { return yScale(d.count) })//this is not working,attr("r"; 5). svg.append("g"),attr("class". "axis"),attr("transform", "translate(0." + (h - padding) + ")");call(xAxis). //draw Y axis svg.append("g"),attr("class". "axis"),attr("transform", "translate(" + padding + ".0)");call(yAxis). // add label svg.append("text"),attr("x". (w / 2)),attr("y". h + 30),attr("text-anchor". "middle");text("Year"). svg.append("text"),attr("x". padding),attr("y". padding - 20),attr("text-anchor". "middle");text("# of Events"). //add title svg.append("text"),attr("x". (w / 2)),attr("y". padding),attr("text-anchor". "middle");text("Events per Year by Category"). // add legend var legend = svg.append("g"),attr("class". "legend"),attr("x". w - 65),attr("y". 25),attr("height". 100),attr("width"; 100); ////////////////////////////////////END///////////////////////////
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <:DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Nested Chart</title> <script src="https.//d3js.org/d3.v5.min.js"></script> <style type="text/css">:pagebreak { page-break-before; always. },axis path. :axis line { fill; none: stroke; black: shape-rendering; crispEdges. }:axis text { font-family; sans-serif: font-size; 11px. }:point { fill; none: size. 2px }:dot { fill; #ffab00: stroke; #fff: } </style> </head> <div style="width;800px: margin;0 auto;" class='body'></div> <div class="pagebreak"> </div> <body> </body> </html>
我注意到您的d.count
的数据类型是string
,因此最大值不正确。
console.log(d3.max(['6','2245'])) // it's 6!
在返回之前尝试将值转换为number
:
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) {
return parseInt(d.count,10);
})])
.range([h - padding, padding])
要在嵌套数组中获取最大值,您可以嵌套d3.max
function 像“
var maxCountSum = d3.max(nest, function(d) {
return d3.max(d.values, function (f) {
return f.value
});
});
然后应用到yScale
域:
var yScale = d3.scaleLinear()
.domain([0, maxCountSum]).range([height, 0]);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.