我想用过滤选项绘制这张堆积的条形图,但是它没有显示出来,我也不知道问题出在哪里。我在控制台中没有任何错误。 Here是完整代码。
在下面,您可以找到我不知道为什么无法可视化条形图的javascript部分:
var csv = [
{
"Year": 2017,
"State": "AL",
"Under 5 Years": 552,
"5 to 13 Years": 259,
"14 to 17 Years": 310
},
{
"Year": 2017,
"State": "AK",
"Under 5 Years": 856,
"5 to 13 Years": 421,
"14 to 17 Years": 520
}
]
function chart(csv) {
var keys = csv.columns.slice(2);
var year = [...new Set(csv.map(d => d.Year))]
var states = [...new Set(csv.map(d => d.State))]
var options = d3.select("#year").selectAll("option")
.data(year)
.enter().append("option")
.text(d => d)
var svg = d3.select("#chart"),
margin = {top: 35, left: 35, bottom: 0, right: 0},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([margin.left, width - margin.right])
.padding(0.1)
var y = d3.scaleLinear()
.rangeRound([height - margin.bottom, margin.top])
var xAxis = svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.attr("class", "x-axis")
var yAxis = svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.attr("class", "y-axis")
var z = d3.scaleOrdinal()
.range(["steelblue", "darkorange", "lightblue"])
.domain(keys);
update(d3.select("#year").property("value"), 0)
function update(input, speed) {
var data = csv.filter(f => f.Year == input)
data.forEach(function(d) {
d.total = d3.sum(keys, k => +d[k])
return d
})
y.domain([0, d3.max(data, d => d3.sum(keys, k => +d[k]))]).nice();
svg.selectAll(".y-axis").transition().duration(speed)
.call(d3.axisLeft(y).ticks(null, "s"))
data.sort(d3.select("#sort").property("checked")
? (a, b) => b.total - a.total
: (a, b) => states.indexOf(a.State) - states.indexOf(b.State))
x.domain(data.map(d => d.State));
svg.selectAll(".x-axis").transition().duration(speed)
.call(d3.axisBottom(x).tickSizeOuter(0))
var group = svg.selectAll("g.layer")
.data(d3.stack().keys(keys)(data), d => d.key)
group.exit().remove()
group.enter().append("g")
.classed("layer", true)
.attr("fill", d => z(d.key));
var bars = svg.selectAll("g.layer").selectAll("rect")
.data(d => d, e => e.data.State);
bars.exit().remove()
bars.enter().append("rect")
.attr("width", x.bandwidth())
.merge(bars)
.transition().duration(speed)
.attr("x", d => x(d.data.State))
.attr("y", d => y(d[1]))
.attr("height", d => y(d[0]) - y(d[1]))
var text = svg.selectAll(".text")
.data(data, d => d.State);
text.exit().remove()
text.enter().append("text")
.attr("class", "text")
.attr("text-anchor", "middle")
.merge(text)
.transition().duration(speed)
.attr("x", d => x(d.State) + x.bandwidth() / 2)
.attr("y", d => y(d.total) - 5)
.text(d => d.total)
}
var select = d3.select("#year")
.on("change", function() {
update(this.value, 750)
})
var checkbox = d3.select("#sort")
.on("click", function() {
update(select.property("value"), 750)
})
}
chart(csv);
答案 0 :(得分:1)
“控制台中没有错误” 是什么意思?错误明确指出:
未定义不是对象(评估“ csv.columns.slice”)
正在发生的事情是您正在使用名为columns
的属性,但是csv
中没有这样的属性,它只是一个JavaScript数组。加载外部CSV文件时,columns
属性是由d3.csv
和d3.csvParse
(在您的版本为d3.csv.parse
)中创建的。
话虽如此,只要做:
var keys = Object.keys(csv[0]).slice(2);
这是上述D3方法创建的columns
属性的简单替代方案。这就是它的作用:顾名思义,Object.keys
是指定对象的密钥。由于数组中的所有对象都具有相同的键,因此我们只需要第一个键,因此就是csv[0]
。然后,使用拼接,删除前两个键,即Year
和State
:
var csv = [{
"Year": 2017,
"State": "AL",
"Under 5 Years": 552,
"5 to 13 Years": 259,
"14 to 17 Years": 310
}];
console.log(Object.keys(csv[0]).slice(2));
如您所见,重要信息是要删除的元素的索引。
这是您的代码,只有该更改:
var csv = [
{
"Year": 2017,
"State": "AL",
"Under 5 Years": 552,
"5 to 13 Years": 259,
"14 to 17 Years": 310
},
{
"Year": 2017,
"State": "AK",
"Under 5 Years": 856,
"5 to 13 Years": 421,
"14 to 17 Years": 520
},
{
"Year": 2017,
"State": "AZ",
"Under 5 Years": 828,
"5 to 13 Years": 362,
"14 to 17 Years": 515
},
{
"Year": 2017,
"State": "AR",
"Under 5 Years": 343,
"5 to 13 Years": 157,
"14 to 17 Years": 202
},
{
"Year": 2017,
"State": "CA",
"Under 5 Years": 449,
"5 to 13 Years": 215,
"14 to 17 Years": 270
},
{
"Year": 2017,
"State": "CO",
"Under 5 Years": 587,
"5 to 13 Years": 261,
"14 to 17 Years": 358
},
{
"Year": 2017,
"State": "CT",
"Under 5 Years": 403,
"5 to 13 Years": 196,
"14 to 17 Years": 211
},
{
"Year": 2017,
"State": "DE",
"Under 5 Years": 794,
"5 to 13 Years": 474,
"14 to 17 Years": 593
},
{
"Year": 2018,
"State": "AL",
"Under 5 Years": 310,
"5 to 13 Years": 552,
"14 to 17 Years": 259
},
{
"Year": 2018,
"State": "AK",
"Under 5 Years": 520,
"5 to 13 Years": 556,
"14 to 17 Years": 421
},
{
"Year": 2018,
"State": "AZ",
"Under 5 Years": 515,
"5 to 13 Years": 828,
"14 to 17 Years": 362
},
{
"Year": 2018,
"State": "AR",
"Under 5 Years": 202,
"5 to 13 Years": 343,
"14 to 17 Years": 157
},
{
"Year": 2018,
"State": "CA",
"Under 5 Years": 270,
"5 to 13 Years": 449,
"14 to 17 Years": 215
},
{
"Year": 2018,
"State": "CO",
"Under 5 Years": 358,
"5 to 13 Years": 587,
"14 to 17 Years": 261
},
{
"Year": 2018,
"State": "CT",
"Under 5 Years": 211,
"5 to 13 Years": 403,
"14 to 17 Years": 196
},
{
"Year": 2018,
"State": "DE",
"Under 5 Years": 593,
"5 to 13 Years": 994,
"14 to 17 Years": 474
}
]
function chart(csv) {
var keys = Object.keys(csv[0]).slice(2);
var year = [...new Set(csv.map(d => d.Year))]
var states = [...new Set(csv.map(d => d.State))]
var options = d3.select("#year").selectAll("option")
.data(year)
.enter().append("option")
.text(d => d)
var svg = d3.select("#chart"),
margin = {top: 35, left: 35, bottom: 0, right: 0},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([margin.left, width - margin.right])
.padding(0.1)
var y = d3.scaleLinear()
.rangeRound([height - margin.bottom, margin.top])
var xAxis = svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.attr("class", "x-axis")
var yAxis = svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.attr("class", "y-axis")
var z = d3.scaleOrdinal()
.range(["steelblue", "darkorange", "lightblue"])
.domain(keys);
update(d3.select("#year").property("value"), 0)
function update(input, speed) {
var data = csv.filter(f => f.Year == input)
data.forEach(function(d) {
d.total = d3.sum(keys, k => +d[k])
return d
})
y.domain([0, d3.max(data, d => d3.sum(keys, k => +d[k]))]).nice();
svg.selectAll(".y-axis").transition().duration(speed)
.call(d3.axisLeft(y).ticks(null, "s"))
data.sort(d3.select("#sort").property("checked")
? (a, b) => b.total - a.total
: (a, b) => states.indexOf(a.State) - states.indexOf(b.State))
x.domain(data.map(d => d.State));
svg.selectAll(".x-axis").transition().duration(speed)
.call(d3.axisBottom(x).tickSizeOuter(0))
var group = svg.selectAll("g.layer")
.data(d3.stack().keys(keys)(data), d => d.key)
group.exit().remove()
group.enter().append("g")
.classed("layer", true)
.attr("fill", d => z(d.key));
var bars = svg.selectAll("g.layer").selectAll("rect")
.data(d => d, e => e.data.State);
bars.exit().remove()
bars.enter().append("rect")
.attr("width", x.bandwidth())
.merge(bars)
.transition().duration(speed)
.attr("x", d => x(d.data.State))
.attr("y", d => y(d[1]))
.attr("height", d => y(d[0]) - y(d[1]))
var text = svg.selectAll(".text")
.data(data, d => d.State);
text.exit().remove()
text.enter().append("text")
.attr("class", "text")
.attr("text-anchor", "middle")
.merge(text)
.transition().duration(speed)
.attr("x", d => x(d.State) + x.bandwidth() / 2)
.attr("y", d => y(d.total) - 5)
.text(d => d.total)
}
var select = d3.select("#year")
.on("change", function() {
update(this.value, 750)
})
var checkbox = d3.select("#sort")
.on("click", function() {
update(select.property("value"), 750)
})
}
chart(csv);
.axis .domain {
display: none;
}
<html>
<head>
<meta charset ="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
body {
margin: auto;
width: 650px;
font: 12px arial;
}
</style>
</head>
<body>
<svg id="chart" width="650" height="400"></svg>
Select year:
<select id="year"></select>
<input type="checkbox" id="sort">
Toggle sort
</body>
</html>