Skip to content

Instantly share code, notes, and snippets.

@maegul
Last active April 27, 2019 23:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maegul/7d8e7342c649fdc077a6984e52da4b62 to your computer and use it in GitHub Desktop.
Save maegul/7d8e7342c649fdc077a6984e52da4b62 to your computer and use it in GitHub Desktop.
Dynamic BeeSwarm
license: gpl-3.0
height: 500
scrolling: no
border: yes

Dynamic Bee Swarm Plot

Three univariate datasets of the same size, each drawn from slightly different normal distributions.

Using d3 force directed simulation, a dynamic beeswarm plot is used. The value of the data is represented as a force pushing each circle to a horizontal or 'x' location. Another force is pushing all circles toward the horizontal line in the virtical or 'y' direction.

The buttons change the dataset, which is updated by changing the x force in the force layout. As the simulation is running immediately after the update, the movement of the circles is effectively animated through the tick function that runs for each tick of the simulation.

Circles may be clicked to highlight and track them through the animation.

Force layout simulation is allowed to run for 8 seconds without decay before a moderate decay is added to smoothly kill the simulation.

Force strengths and alphadecay rates often need manual tweeking (in my experience).

[{"three": 4.263546482227679, "two": 2.4589369984627387, "one": 0.83829985051382361}, {"three": 2.2785021121607705, "two": 0.856088491892292, "one": 1.2582398770239278}, {"three": 3.9060385952844845, "two": 0.8355944061060497, "one": 1.2206203629661856}, {"three": 4.70680974729798, "two": 1.0793225376795446, "one": 0.35558300549944211}, {"three": 0.6318898056999386, "two": 0.8885811988035506, "one": -1.2854026701038324}, {"three": 4.146526293475539, "two": 2.353281662775892, "one": 0.44112685918353772}, {"three": 4.357043758620638, "two": 0.43507916958379167, "one": -0.8180137164701069}, {"three": 2.541406442927696, "two": 2.6995398758629077, "one": -0.68810696115449288}, {"three": 1.5346825838028557, "two": 1.1083645828702307, "one": 1.1698851276218936}, {"three": 2.730708952668105, "two": 0.45992919941586885, "one": 0.38030136941678877}, {"three": 3.34211705368414, "two": 0.8542654740703389, "one": -1.7133122797825715}, {"three": 2.614741007228651, "two": -0.867438064462281, "one": -0.11505720426878975}, {"three": 4.370778280544622, "two": 1.4125719682100002, "one": -1.1666419298804067}, {"three": 3.093774105889726, "two": 0.7904043446845878, "one": -2.7155831085069493}, {"three": 2.547933149195279, "two": 0.9461506708618969, "one": -1.3329754674592758}, {"three": 4.132680913531688, "two": 0.8740005809223094, "one": -1.3817979632020692}, {"three": 1.6247162463181204, "two": 0.2952354987046033, "one": -1.1572617653089396}, {"three": 3.9291943572647425, "two": 0.9226833218588283, "one": 0.33729668177871591}, {"three": 4.826970522537833, "two": 1.3751468598972953, "one": 1.1762647164687179}, {"three": 1.2424862248823514, "two": 0.999185718085598, "one": 0.33489574640177155}, {"three": 4.73975395789525, "two": 0.39918419980908926, "one": 0.51832498050640885}, {"three": 1.8820225783766769, "two": 1.6508910084662134, "one": 1.0840061024369774}, {"three": 4.708612698747654, "two": 1.324682425218582, "one": -1.1066024421889864}, {"three": 2.789205586481188, "two": 3.0825011639425934, "one": 0.89347822181294723}, {"three": 1.091613482725723, "two": 1.106069988764536, "one": 1.1850502550597306}, {"three": 5.015421521696837, "two": 1.5891828672615453, "one": -0.6980681383900883}, {"three": 2.4907435498146593, "two": 1.8035034862208306, "one": 0.57393031288810947}, {"three": 2.81763793325365, "two": 1.1923407143602947, "one": 0.4621397020929211}, {"three": 2.023056858829425, "two": -0.49194253473289606, "one": -0.27507930758629351}, {"three": 4.0626031870629316, "two": 4.127213915504429, "one": 1.2206373340271011}, {"three": 1.7425779449360776, "two": 0.9861634297254751, "one": 0.36847178654972595}, {"three": 1.29650672610429, "two": 1.2024181466057073, "one": -2.1013373986883161}, {"three": 3.5905273681415513, "two": 1.2206420332740189, "one": -0.92168877330406862}, {"three": 2.912006703319252, "two": 0.1189105531214314, "one": -1.3754639086363574}, {"three": 1.5216763352428702, "two": 1.8607634184836672, "one": -0.91803509508588543}, {"three": 0.8138061087366771, "two": 1.8588665950803573, "one": -0.54882950983938095}, {"three": 3.3475461852609874, "two": -0.41845680077241276, "one": -0.95005193102681562}, {"three": 3.3945777592773414, "two": -0.3528625968283352, "one": 2.1436573452767846}, {"three": 4.18372360181714, "two": 1.0172014975394692, "one": -0.28305915603786763}, {"three": 3.703110945773227, "two": 0.14818023398540658, "one": 0.87830534307676}, {"three": 4.563238539216607, "two": 1.2631880865678262, "one": 0.061594386663227189}, {"three": 2.4934357310682347, "two": 0.9563902007055716, "one": -0.91503283442772043}, {"three": 2.6751758005308717, "two": 1.5040586758487928, "one": 0.033302501496081729}, {"three": 3.4550687226158683, "two": 0.6418794551808733, "one": 0.27706528762087113}, {"three": 6.533824369927789, "two": 2.161844977442895, "one": 0.25905498864939569}, {"three": 1.496916751444897, "two": 0.31091606532596616, "one": 0.41994131816562313}, {"three": 5.145634816932612, "two": 1.1395534564421665, "one": -0.97419292689711068}, {"three": 2.5855136320769216, "two": 2.776388438921536, "one": 1.5488796735820098}, {"three": 2.2315760166607017, "two": 2.148661687829385, "one": 0.072118022998094305}, {"three": 2.6402587033084255, "two": 1.3211204157755652, "one": -1.6151025786427906}]
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style type="text/css">
.circ {
fill: #bc53ff;
fill-opacity: 0.6;
stroke: black;
cursor: pointer;
}
</style>
</head>
<body>
<script type="text/javascript">
var width = 960,
height = 250,
radius = 10;
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
svg.append("line", 'svg')
.classed('main_line', true)
.attr("x1", 0)
.attr("y1", height/2)
.attr("x2", width)
.attr("y2", height/2)
.attr("stroke-width", 1.5)
.attr("stroke", "#A3A0A6");
var x = d3.scaleLinear()
.range([0, width])
.domain([-3, 8]);
var data_set = 'one';
d3.json('data.json', function(data){
console.log(data);
function tick(){
d3.selectAll('.circ')
.attr('cx', function(d){return d.x})
.attr('cy', function(d){return d.y})
}
svg.selectAll('.circ')
.data(data)
.enter().append('circle').classed('circ', true)
.attr('r', radius)
.attr('cx', function(d){return x(d[data_set]);})
.attr('cy', function(){return height/2;})
.on('click', function(){
var circ = d3.select(this);
circ.style('stroke', '#56C6D8')
.style('stroke-width', 3)
})
var simulation = d3.forceSimulation(data)
.force('x', d3.forceX(function(d){
return x(d[data_set])
}).strength(0.99)
)
.force('y', d3.forceY(height/2).strength(0.05))
.force('collide', d3.forceCollide(radius))
.alphaDecay(0)
.alpha(0.12)
.on('tick', tick)
var init_decay;
init_decay = setTimeout(function(){
console.log('init alpha decay')
simulation.alphaDecay(0.1);
}, 8000);
var buttons = d3.select('body').append('div');
buttons.append('button').text('One').attr('value', 'one').classed('d_sel', true)
buttons.append('button').text('Two').attr('value', 'two').classed('d_sel', true)
buttons.append('button').text('Three').attr('value', 'three').classed('d_sel', true)
d3.selectAll('.d_sel').on('click', function(){
data_set = this.value;
console.log(data_set)
simulation.force('x', d3.forceX(function(d){
return x(d[data_set])
}))
simulation
.alphaDecay(0)
.alpha(0.12)
.restart()
clearTimeout(init_decay);
init_decay = setTimeout(function(){
console.log('init alpha decay');
simulation.alphaDecay(0.1);
}, 8000);
})
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment