我想在 d3.js 中创建一个点图,它看起来有点像条形图,但有堆叠的点而不是条形。
这个问题跟我想的差不多:D3.js (Wilkinson type) Dot Plot Example
我已经使用该代码尝试完成我想要的,但我被卡住了。
根据我的 CSV 文件中的苹果数据,这是一张显示我想要的图像:
我目前的代码如下。当我使用 console.log d3.range(d.apples) 时,我看到我每天都有一个数组,这就是我想要的。但是我不知道如何为数组中的每个点画一个圆,其中 x 轴值是日期 #,y 轴值是数组中的位置 + 1。
非常欢迎任何帮助!谢谢。
代码:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var chart = svg.append("g")
.attr("id", "chart");
d3.csv("fruit.csv", function(error, data) {
if (error) throw error;
// console.log("data: ", data);
data.forEach(function(d) {
d.day = +d.day;
d.apples = +d.apples;
});
x.domain([0,4]);
y.domain([0,6]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount")
chart.selectAll("g")
.data(data)
.enter()
.append("g")
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d, i) { return x(d.day); })
//.attr("cy", function(d,i){ return y(d.apples); })
.attr("cy", function(d,i,j){
var range = d3.range(d.apples);
console.log("apple range: ", range);
return y(range[i])
})
.style("fill", "blue")
.style("opacity", .5);
});
</script>
数据(水果.csv):
"day","apples"
1,3
2,6
3,1
4,2
最佳答案
有几种方法可以做你想做的事。我相信最传统的方法是嵌套数据并附加多个组,每个组对应一个水果。但是,为了简单起见,我想提出一种不同的方法:让我们更改您的数据,将其展平,这样,在每个对象中,我们只有一个水果:
originalData.forEach(function(d) {
data.push({
day: d.day,
fruit: "apples",
amount: +d.apples
});
data.push({
day: d.day,
fruit: "pears",
amount: +d.pears
});
data.push({
day: d.day,
fruit: "oranges",
amount: +d.oranges
});
});
其中 originalData
是数据数组的当前状态。
这样做,你的数据会变成这样:
[{
"day": "1",
"fruit": "apples",
"amount": 3
}, {
"day": "1",
"fruit": "pears",
"amount": 6
}, {
"day": "1",
"fruit": "oranges",
"amount": 3
}, {
"day": "2",
"fruit": "apples",
"amount": 6
},//etc...
]
然后,我们设置天数的顺序:
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
.domain(data.map(d => d.day));
最后,我们绘制圆圈:
var dots = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.day);
})
.attr("cy", function(d) {
return y(d.amount)
})
.style("fill", function(d) {
return color(d.fruit)
})
.style("opacity", .5);
这是一个演示(我放了一些标题,所以你可以将鼠标悬停在点上以查看水果和数量):
var rawData = d3.csv.parse(d3.select("#csv").text());
var data = [];
rawData.forEach(function(d) {
data.push({
day: d.day,
fruit: "apples",
amount: +d.apples
});
data.push({
day: d.day,
fruit: "pears",
amount: +d.pears
});
data.push({
day: d.day,
fruit: "oranges",
amount: +d.oranges
});
});
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10()
.domain(["apples", "pears", "oranges"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var chart = svg.append("g")
.attr("id", "chart");
x.domain(data.map(d => d.day));
y.domain([0, 10]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount");
var dots = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.day);
})
.attr("cy", function(d) {
return y(d.amount)
})
.style("fill", function(d) {
return color(d.fruit)
})
.style("opacity", .5)
.append("title")
.text(function(d){ return d.fruit + ": " + d.amount});
pre {
display: none;
}
body {
font: 10 px sans - serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples","pears","oranges"
1,3,6,3
2,6,8,2
3,1,0,7
4,2,3,8</pre>
编辑:在评论中进一步澄清后,这就是 OP 想要的:
像你一样附加组,但根据日期翻译它们:
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.day) + ".0)";
});
然后,在点中,使用 d3.range
设置数据:
var dots = groups.selectAll("circle")
.data(function(d){ return d3.range(+d.apples + 1)})
这样,对于每个组,圆圈的数据一直到最大值。例如,如果在给定的一天“苹果”是 3,则数据将是:
[0, 1, 2, 3]
或者,如果“apples”是 6,则数据将是:
[0, 1, 2, 3, 4, 5, 6]
如果您不想要前导零,只需执行 d3.range(1, +d.apples + 1)
。
这是一个演示:
var data = d3.csv.parse(d3.select("#csv").text());
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var chart = svg.append("g")
.attr("id", "chart");
x.domain(data.map(d => d.day));
y.domain([0, 7]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount");
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.day) + ".0)";
});
var dots = groups.selectAll("circle")
.data(function(d) {
return d3.range(1, +d.apples + 1)
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cy", function(d) {
return y(d)
})
.style("fill", "blue")
.style("opacity", .5);
pre {
display: none;
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples"
1,3
2,6
3,1
4,2</pre>
关于javascript - 使用显示堆叠点的 d3.js 创建点图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41215085/