I am using this example to make scatter plot:
https://www.d3-graph-gallery.com/graph/boxplot_show_individual_points.html
Now this example uses jitter to randomize x position of the dots for demonstration purpose, but my goal is to make these dots in that way so they don't collide and to be in the same row if there is collision.
Best example of what I am trying to do (visually) is some sort of beeswarm where data is represented like in this fiddle:
https://jsfiddle.net/n444k759/4/
Snippet of first example:
// 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>
I tried to make something like this:
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")
but it does not even prevent collision and I am a bit confused with it.
I also tried to modify plot from this example:
http://bl.ocks.org/asielen/92929960988a8935d907e39e60ea8417
where beeswarm looks exactly what I need to achieve. But this code is way too expanded as it is made to fit the purpose of reusable charts and I can't track what exact formula is used to achieve this:
from D3js Grouped Scatter plot with no collision
No comments:
Post a Comment