gpt4 book ai didi

javascript - D3js 没有碰撞的分组散点图

转载 作者:数据小太阳 更新时间:2023-10-29 04:39:34 36 4
gpt4 key购买 nike

我正在使用这个例子来制作散点图:

https://www.d3-graph-gallery.com/graph/boxplot_show_individual_points.html

现在这个例子使用抖动来随机化点的 x 位置以用于演示目的,但我的目标是以这种方式制作这些点,这样它们就不会发生碰撞并且在发生碰撞时位于同一行。

我正在尝试做的(视觉上的)最好的例子是某种蜂群,其中数据像这个 fiddle 一样表示:

https://jsfiddle.net/n444k759/4/

第一个例子的片段:

// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 40},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");

// Read the data and compute summary statistics for each specie
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv", function(data) {

// Compute quartiles, median, inter quantile range min and max --> these info are then used to draw the box.
var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
.key(function(d) { return d.Species;})
.rollup(function(d) {
q1 = d3.quantile(d.map(function(g) { return g.Sepal_Length;}).sort(d3.ascending),.25)
median = d3.quantile(d.map(function(g) { return g.Sepal_Length;}).sort(d3.ascending),.5)
q3 = d3.quantile(d.map(function(g) { return g.Sepal_Length;}).sort(d3.ascending),.75)
interQuantileRange = q3 - q1
min = q1 - 1.5 * interQuantileRange
max = q3 + 1.5 * interQuantileRange
return({q1: q1, median: median, q3: q3, interQuantileRange: interQuantileRange, min: min, max: max})
})
.entries(data)

// Show the X scale
var x = d3.scaleBand()
.range([ 0, width ])
.domain(["setosa", "versicolor", "virginica"])
.paddingInner(1)
.paddingOuter(.5)
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))

// Show the Y scale
var y = d3.scaleLinear()
.domain([3,9])
.range([height, 0])
svg.append("g").call(d3.axisLeft(y))

// Show the main vertical line
svg
.selectAll("vertLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d){return(x(d.key))})
.attr("x2", function(d){return(x(d.key))})
.attr("y1", function(d){return(y(d.value.min))})
.attr("y2", function(d){return(y(d.value.max))})
.attr("stroke", "black")
.style("width", 40)

// rectangle for the main box
var boxWidth = 100
svg
.selectAll("boxes")
.data(sumstat)
.enter()
.append("rect")
.attr("x", function(d){return(x(d.key)-boxWidth/2)})
.attr("y", function(d){return(y(d.value.q3))})
.attr("height", function(d){return(y(d.value.q1)-y(d.value.q3))})
.attr("width", boxWidth )
.attr("stroke", "black")
.style("fill", "#69b3a2")

// Show the median
svg
.selectAll("medianLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d){return(x(d.key)-boxWidth/2) })
.attr("x2", function(d){return(x(d.key)+boxWidth/2) })
.attr("y1", function(d){return(y(d.value.median))})
.attr("y2", function(d){return(y(d.value.median))})
.attr("stroke", "black")
.style("width", 80)

var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) { return x(d.Species); }))
// .force("y", d3.forceX(function(d) { return y(d.Sepal_lenght) }))
.force("collide", d3.forceCollide()
.strength(1)
.radius(4+1))
.stop();

for (var i = 0; i < data.length; ++i) simulation.tick();

// Add individual points with jitter
var jitterWidth = 50
svg
.selectAll("points")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d){return( d.x )})
.attr("cy", function(d){return(y(d.Sepal_Length))})
.attr("r", 4)
.style("fill", "white")
.attr("stroke", "black")


})
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

我试着做这样的东西:

var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) { return x(d.Species); }))
.force("collide", d3.forceCollide(4)
.strength(1)
.radius(4+1))
.stop();

for (var i = 0; i < 120; ++i) simulation.tick();

// Append circle points
svg.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d){
return(x(d.x))
})
.attr("cy", function(d){
return(y(d.y))
})
.attr("r", 4)
.attr("fill", "white")
.attr("stroke", "black")

但它甚至不能防止碰撞,我对此有点困惑。

我还尝试修改这个例子中的情节:

http://bl.ocks.org/asielen/92929960988a8935d907e39e60ea8417

beeswarm 看起来正是我需要实现的目标。但是这段代码过于扩展,因为它是为了满足可重用图表的目的而设计的,我无法追踪具体使用了什么公式来实现这一点:

enter image description here

任何帮助都会很棒..谢谢

最佳答案

这是一个简单的示例,它结合了您的 beeswarm 示例的想法和您的初始箱线图。我在下面评论了棘手的部分:

<!DOCTYPE html>
<html>

<head>
</head>

<body>
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

<script>
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 40
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");

// Read the data and compute summary statistics for each specie
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv", function(data) {

// Compute quartiles, median, inter quantile range min and max --> these info are then used to draw the box.
var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
.key(function(d) {
return d.Species;
})
.rollup(function(d) {
q1 = d3.quantile(d.map(function(g) {
return g.Sepal_Length;
}).sort(d3.ascending), .25)
median = d3.quantile(d.map(function(g) {
return g.Sepal_Length;
}).sort(d3.ascending), .5)
q3 = d3.quantile(d.map(function(g) {
return g.Sepal_Length;
}).sort(d3.ascending), .75)
interQuantileRange = q3 - q1
min = q1 - 1.5 * interQuantileRange
max = q3 + 1.5 * interQuantileRange
return ({
q1: q1,
median: median,
q3: q3,
interQuantileRange: interQuantileRange,
min: min,
max: max
})
})
.entries(data)

// Show the X scale
var x = d3.scaleBand()
.range([0, width])
.domain(["setosa", "versicolor", "virginica"])
.paddingInner(1)
.paddingOuter(.5)
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))

// Show the Y scale
var y = d3.scaleLinear()
.domain([3, 9])
.range([height, 0])
svg.append("g").call(d3.axisLeft(y))

// Show the main vertical line
svg
.selectAll("vertLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d) {
return (x(d.key))
})
.attr("x2", function(d) {
return (x(d.key))
})
.attr("y1", function(d) {
return (y(d.value.min))
})
.attr("y2", function(d) {
return (y(d.value.max))
})
.attr("stroke", "black")
.style("width", 40)

// rectangle for the main box
var boxWidth = 100
svg
.selectAll("boxes")
.data(sumstat)
.enter()
.append("rect")
.attr("x", function(d) {
return (x(d.key) - boxWidth / 2)
})
.attr("y", function(d) {
return (y(d.value.q3))
})
.attr("height", function(d) {
return (y(d.value.q1) - y(d.value.q3))
})
.attr("width", boxWidth)
.attr("stroke", "black")
.style("fill", "#69b3a2")

// Show the median
svg
.selectAll("medianLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d) {
return (x(d.key) - boxWidth / 2)
})
.attr("x2", function(d) {
return (x(d.key) + boxWidth / 2)
})
.attr("y1", function(d) {
return (y(d.value.median))
})
.attr("y2", function(d) {
return (y(d.value.median))
})
.attr("stroke", "black")
.style("width", 80)

var r = 8;
// create a scale that'll return a discreet value
// so that close y values fall in a line
var yPtScale = y.copy()
.range([Math.floor(y.range()[0] / r), 0])
.interpolate(d3.interpolateRound)
.domain(y.domain());

// bucket the data
var ptsObj = {};
data.forEach(function(d,i) {
var yBucket = yPtScale(d.Sepal_Length);
if (!ptsObj[d.Species]){
ptsObj[d.Species] = {};
}
if (!ptsObj[d.Species][yBucket]){
ptsObj[d.Species][yBucket] = [];
}
ptsObj[d.Species][yBucket].push({
cy: yPtScale(d.Sepal_Length) * r,
cx: x(d.Species)
});
});

// determine the x position
for (var x in ptsObj){
for (var row in ptsObj[x]) {
var v = ptsObj[x][row], // array of points
m = v[0].cx, // mid-point
l = m - (((v.length / 2) * r) - r/2); // left most position based on count of points in the bucket

v.forEach(function(d,i){
d.cx = l + (r * i); // x position
});
}
}

// flatten the data structure
var flatData = Object.values(ptsObj)
.map(function(d){return Object.values(d)})
.flat(2);

svg
.selectAll("points")
.data(flatData)
.enter()
.append("circle")
.attr("cx", function(d) {
return d.cx;
})
.attr("cy", function(d) {
return d.cy;
})
.attr("r", 4)
.style("fill", "white")
.attr("stroke", "black")


})
</script>
</body>

</html>

关于javascript - D3js 没有碰撞的分组散点图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56212943/

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