我想在d3.js中创建一个圆点图,它看起来有点像条形图,但是有堆叠的点而不是条形。
这个问题与我想要的类似:D3.js (Wilkinson type) Dot Plot Example
我已经用这段代码来尝试完成我想要的东西,但是我被卡住了。
我到目前为止的代码如下。当我在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>
数据(fruit.csv):
"day","apples"
1,3
2,6
3,1
4,2
答案 0 :(得分: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});
&#13;
pre {
display: none;
}
body {
font: 10 px sans - serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
&#13;
<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>
&#13;
编辑:在评论中进一步澄清之后,这就是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)})
这样,对于每个组,圆圈的数据一直到最大值。例如,如果在某一天&#34;苹果&#34;是3,数据将是:
[0, 1, 2, 3]
或者,如果&#34; apples&#34;是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);
&#13;
pre {
display: none;
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
&#13;
<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>
&#13;