Skip to content

Instantly share code, notes, and snippets.

@osoken
Last active May 10, 2017 16:55
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 osoken/447febbc7ec374a6ab6d to your computer and use it in GitHub Desktop.
Save osoken/447febbc7ec374a6ab6d to your computer and use it in GitHub Desktop.
Dot bar chart

点の集まりで棒グラフを表現

矢印キーの左右か、下部の細長い長方形をクリックすることで点が移り変わる。

year A B C D E F G
0 401 150 0 144 48 410 803
1 419 299 90 141 80 180 802
2 468 440 97 95 48 42 860
3 585 459 100 99 48 71 702
4 462 634 89 80 44 104 670
5 423 233 81 84 19 361 882
<!DOCTYPE html>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
<body style='margin:0;padding:0;'>
<script>
var dim = {width: 960, height: 500};
var margin = {top: 10, bottom: 50, left: 50, right: 10};
var inputHeight = 20;
var numberFormat = d3.format('.0f');
dim.graphWidth = dim.width - margin.left - margin.right;
dim.graphHeight = dim.height - margin.top - margin.bottom;
d3.select('body').on('keydown',function()
{
if (d3.event.which === 39)
{
next();
}
if (d3.event.which === 37)
{
prev();
}
});
var svg = d3.select('body').append('svg')
.attr({width: dim.width, height: dim.height})
.style({margin:0,padding:0});
var axisLayer = svg.append('g').attr('transform','translate(' + margin.left + ',' + margin.top + ')');
var graphLayer = svg.append('g').attr('transform','translate(' + margin.left + ',' + margin.top + ')');
var inputLayer = svg.append('g').attr('transform','translate(0,' + (dim.height - inputHeight) + ')');
var xScale = d3.scale.ordinal().rangeBands([0,dim.graphWidth],0.05);
var xLocalScale = d3.scale.ordinal();
var yScale = d3.scale.ordinal().rangePoints([dim.graphHeight, 0]);
var colorScale = d3.scale.category10();
var inputScale = d3.scale.ordinal().rangeBands([0,dim.width-margin.right]);
var xAxis = d3.svg.axis().orient('bottom').scale(xScale);
var yAxis = d3.svg.axis().orient('left').scale(yScale);
var xAxisObj = axisLayer.append('g')
.attr('transform','translate('+0+','+dim.graphHeight+')')
.attr('class','axis')
.call(xAxis);
var yAxisObj = axisLayer.append('g')
.attr('transform','translate('+0 +','+0+')')
.attr('class','axis')
.call(yAxis);
axisLayer.selectAll('.axis text').style('font','14px "Lucida Grande", Helvetica, Arial, sans-serif');
axisLayer.selectAll('.axis path.domain').style({fill:'none',stroke:'#000000','shape-rendering':'crispEdges'});
axisLayer.selectAll('.axis line').style({fill:'none',stroke:'#000000','shape-rendering':'crispEdges'});
var time = 0;
var yearLabel = 'year';
var radius = 3;
var mar = 0.6;
var barWidth = 16;
var displaydata = [];
var years = [];
var auto = true;
var duration = 2000;
var delayMax = 1000;
trans = function(to)
{
if ( to === time || to < 0 || to >= years.length)
{
return;
}
var current = time;
time = to;
yearTarget = years[time];
var votes = graphLayer.selectAll('.vote')
.filter(function(d){return d[current].label!=d[time].label || d[current].idx!=d[time].idx;})
.transition()
.duration(duration)
.delay(function(d){return Math.random()*delayMax;})
.attr('cx',function(d){return ((d[time].label!=null)?(xScale(d[time].label)+xLocalScale(d[time].idx%barWidth)+radius+mar):(dim.graphWidth/2));})
.attr('cy',function(d){return ((d[time].label!=null)?(yScale(Math.floor((d[time].idx+0.1)/barWidth))-radius-mar):0);})
.style('opacity',function(d){return (d[time].label!=null)?0.8:0.0;})
.style('fill',function(d){return colorScale(d[time].label);});
inputLayer.select('.cursor').transition().duration(duration/2)
.attr('x',function(d){return inputScale(years[time]);});
inputLayer.selectAll('.button text').transition().duration(duration/2)
.style('fill',function(d,i){return (i===time)?'#FFF':'#000';})
}
prev = function()
{
trans(time-1);
}
next = function()
{
trans(time+1);
}
d3.csv('data.csv', function(error,raw)
{
if (error != null)
{
console.log(error);
return;
}
years = d3.set(raw.map(function(d){return d[yearLabel];})).values();
yearTarget = years[0];
var parties = d3.keys(raw[0]).filter(function(d){return d !== yearLabel;});
var partDict = {};
parties.forEach(function(d,i)
{
partDict[d] = i;
});
var sums = {};
var data = {};
years.forEach(function(year)
{
data[year] = parties.map(function(party)
{
return +raw.filter(function(d){return d[yearLabel] === year;})[0][party]||0;
});
sums[year] = d3.sum(data[year]);
});
var max = d3.max(years.map(function(d){return d3.max(data[d]);}));
var nrow = Math.ceil(dim.graphHeight/(2*(radius+mar)));
barWidth = Math.ceil(max/nrow);
yScale.domain(d3.range(nrow));
yAxis.tickValues(d3.range(nrow).filter(function(d){return d%10===0;}));
yAxis.tickFormat(function(d){return (d*barWidth);});
xScale.domain(parties.map(function(d,i){return i;}));
xAxis.tickFormat(function(d){return parties[d];});
xAxisObj.call(xAxis);
yAxisObj.call(yAxis);
xLocalScale.rangeBands([0,xScale.rangeBand()]).domain(d3.range(barWidth));
colorScale.domain(d3.range(parties.length));
inputScale.domain(years);
var currentButton = inputLayer.append('rect')
.attr('class','cursor')
.attr({x:0,y:0,height:inputHeight,width:inputScale.rangeBand()})
.style('stroke','#FFF')
.style('stroke-width',2)
.style('fill','#000');
var buttons = inputLayer.selectAll('.button').data(years).enter().append('g').attr('class','button')
.attr('transform',function(d){return 'translate(' + inputScale(d) + ',' + 0 +')';})
.on('click',function()
{
var s = d3.select(this);
trans(years.indexOf(s.datum()));
});
buttons.append('rect')
.attr({x:0,y:0,height:inputHeight,width:inputScale.rangeBand()})
.style('stroke','#FFF')
.style('stroke-width',2)
.style('fill','rgba(0,0,0,0.1)');
buttons.append('text')
.text(function(d){return d;})
.attr('x',function(d){return inputScale.rangeBand()/2;})
.attr('y',0)
.style('fill',function(d,i){return (i===0)?'#FFF':'#000';})
.style('text-anchor','middle')
.style('font',inputHeight+'px "Lucida Grande", Helvetica, Arial, sans-serif').style('dominant-baseline','text-before-edge');
var summax = d3.max(years.map(function(d){return sums[d];}));
var displaydata = d3.range(summax).map(function(d){return [];});
var indexMargin = 0;
parties.forEach(function(party,partyidx)
{
for (var i=0;i<data[years[0]][partyidx];++i)
{
displaydata[indexMargin+i].push({label:partyidx,idx:i});
}
indexMargin += data[years[0]][partyidx];
});
for (var i=indexMargin;i<summax;++i)
{
displaydata[i].push({label:null,idx:null});
}
d3.range(1,years.length).forEach(function(idx)
{
var year = years[idx];
var lastyear = years[idx-1];
var yearidx = idx;
var pool = [];
var unused = [];
var keep = [];
displaydata.forEach(function(d,i)
{
var copy = {label:d[yearidx-1].label,idx:d[yearidx-1].idx};
d.push(copy);
if ( d[yearidx].label == null)
{
unused.push(i);
}
else
{
if(data[year][d[yearidx].label] <= d[yearidx].idx)
{
pool.push(i);
}
else
{
keep.push(i);
}
}
});
d3.shuffle(pool);
if ( sums[year] - sums[lastyear] > 0 )
{
pool = pool.concat(unused.splice(0,sums[year]-sums[lastyear]));
d3.shuffle(pool);
}
else
{
pool.splice(sums[year]-keep.length).forEach(function(d)
{
displaydata[d][yearidx] = {label:null,idx:null};
});
pool = pool.splice(0,sums[year]-keep.length);
}
var poolmargin = 0;
parties.forEach(function(party)
{
if (data[year][partDict[party]] - data[lastyear][partDict[party]] > 0)
{
for(var i=0;i<(data[year][partDict[party]]-data[lastyear][partDict[party]]);++i)
{
displaydata[pool[poolmargin+i]][yearidx] = {label:partDict[party],idx:i+data[lastyear][partDict[party]]};
};
poolmargin += data[year][partDict[party]]-data[lastyear][partDict[party]];
}
});
});
var votes = graphLayer.selectAll('.vote').data(displaydata).enter().append('circle')
.attr('class','vote')
.attr('r',radius)
.attr('cx',function(d){return ((d[time].label!=null)?(xScale(d[time].label)+xLocalScale(d[time].idx%barWidth)+radius+mar):(dim.graphWidth/2));})
.attr('cy',function(d){return ((d[time].label!=null)?(yScale(Math.floor((d[time].idx+0.1)/barWidth))-radius-mar):0);})
.style('opacity',function(d){return (d[time].label!=null)?0.8:0.0;})
.style('fill',function(d){return colorScale(d[time].label);});
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment