Skip to content

Instantly share code, notes, and snippets.

@rustyb
Created March 19, 2015 16:02
Show Gist options
  • Save rustyb/8e56d790f9a244ee5cbe to your computer and use it in GitHub Desktop.
Save rustyb/8e56d790f9a244ee5cbe to your computer and use it in GitHub Desktop.
Punchcard for Lesotho OSM Mappers
/*! D3punchcard v0.1.0 - MIT license */
;(function (global) { function moduleDefinition( d3 ) {
function D3punchcard( options ) {
// Reverse the data as we draw
// from the bottom up.
this.data = options.data.reverse();
this.element = options.element;
// Find the max value to normalize the size of the circles.
this.max = d3.max( this.data , function(array) {
// we ignore the first element as it is metadata
return d3.max(array.slice(1), function ( obj ) {
// and we only return the interger verion of the value, not the key
return parseFloat( obj.value );
});
});
// set the upperlimit if we have it
// otherwise use the max
if( options.upperLimit ){
this.upperLimit = options.upperLimit;
} else {
this.upperLimit = this.max;
}
return this;
}
D3punchcard.prototype.draw = function( options ){
var _this = this,
margin = 10,
lineHeight = 5,
width = options.width,
paneLeft = 80,
paneRight = width - paneLeft,
sectionHeight = 35,
height = ( sectionHeight * this.data.length ),
sectionWidth = paneRight / this.data[0].length,
circleRadius = 14,
x,
y,
punchcard,
punchcardRow,
xAxis,
rScale;
// X-Axis.
x = d3.scale.linear().domain([0, this.data[0].length-1]).
range([ paneLeft + (sectionWidth / 2) , paneRight + (sectionWidth / 2)]);
// Y-Axis.
y = d3.scale.linear().domain([0, this.data.length-1]).
range([0, height - sectionHeight]);
rScale = d3.scale.linear()
.domain([2, this.upperLimit, this.max])
.range([2, circleRadius, circleRadius]);
// these functions hide and show the circles and text values
function handleRowMouseover(){
var g = d3.select(this).node().parentNode;
d3.select(g).selectAll('circle').style('display','none');
d3.select(g).selectAll('text.value').style('display','block');
}
function handleRowMouseout(){
var g = d3.select(this).node().parentNode;
d3.select(g).selectAll('circle').style('display','block');
d3.select(g).selectAll('text.value').style('display','none');
d3.select(g).selectAll('text.value').lastChild.style('display','block');
}
// The main SVG element.
punchcard = d3.select(this.element)
.html('')
.append('svg')
.attr('width', width )
.attr('height', height + (margin*3))
.append('g');
// create the x axis holder
xAxis = punchcard.selectAll('.row')
.data( [this.data[0].slice(1)] )
.enter()
.append('g')
.attr('class', 'xaxis');
// create the x axis line
xAxis.
append('line').
attr('x1', 0).
attr('x2', width).
attr('y1', (margin * 3)).
attr('y2', (margin * 3)).
style('stroke-width', 1).
style('stroke', '#efefef');
// create x-axis ticks
xAxis.
selectAll('line.tick').
data(function(d, i ) {
return d;
}).
enter().
append('line').
attr('class', 'tick').
attr('x1', function(d,i) { return paneLeft + x(i); }).
attr('x2', function(d,i) { return paneLeft + x(i); }).
attr('y1', function (d, i) {
return margin * 2;
}).
attr('y2', function (d, i) {
return (margin * 3);
}).
style('stroke-width', 1).
style('stroke', '#efefef');
// create x-axis tick text.
xAxis.
selectAll('.rule').
data(function(d, i ) {
return d;
}).
enter().
append('text').
attr('class', 'rule').
attr('x', function(d, i) {
return paneLeft + x(i);
}).
attr('y', margin + lineHeight).
attr('text-anchor', 'middle').
attr('style', 'font-weight:bold;text-transform:capitalize;').
text(function(d) {
return d.key;
});
// create rows
punchcardRow = punchcard.selectAll('.row')
.data( this.data )
.enter()
.append('g')
.attr('class', 'row')
.attr('transform', function(d, i) {
var ty = height - y(i) - (sectionHeight/2) + (margin*3);
return 'translate(0, ' + ty + ')';
});
// create row divinding lines
punchcardRow.
selectAll('line').
data([0]).
enter().
append('line').
attr('x1', 0).
attr('x2', width).
attr('y1', (sectionHeight/2)).
attr('y2', (sectionHeight/2)).
style('stroke-width', 1).
style('stroke', '#efefef');
// create row headers
punchcardRow.
selectAll('.textheader').
data( function(d, i ) {
// we only return the first element of each array
// which contains the header text
return [d[0]];
} ).
enter().
append('text').
attr('x', 0).
attr('y', function (d, i) {
return lineHeight;
}).
attr('class', 'textheader').
attr('text-anchor', 'left').
text(function (d, i) {
return d.value;
})
.on('mouseover', handleRowMouseover)
.on('mouseout', handleRowMouseout);
// draw circles for each row
punchcardRow.
selectAll('circle').
data( function(d, i ) {
return d.slice(1);
} ).
enter().
append('circle').
style('fill', '#888').
attr('r', function(d, i) {
return rScale( parseFloat( d.value ) );
}).
attr('transform', function(d, i) {
var tx = paneLeft + x(i);
return 'translate(' + tx + ', 0)';
});
// draw text values for each row
punchcardRow.
selectAll('text.value').
data( function(d, i ) {
return d.slice(1);
} ).
enter().
append('text').
attr('class', 'value').
style('display','none').
text(function (d, i) {
return d.value;
}).
attr('text-anchor', 'middle').
attr('x', function (d, i) {
return paneLeft + x(i);
}).
attr('y', function (d, i) {
return lineHeight;
});
return this;
};
// to be called when closing a view
// in which this object has been created
D3punchcard.prototype.destroy = function () {
// remove event listeners
d3.select(this.element)
.selectAll('text.textheader')
.on('mouseover', null)
.on('mouseout', null);
};
/**
* Expose D3punchcard
*/
return D3punchcard;
// ---------------------------------------------------------------------------
} if (typeof exports === 'object') {
// node export
module.exports = moduleDefinition(require('d3'));
} else if (typeof define === 'function' && define.amd) {
// amd anonymous module registration
define(['d3'], moduleDefinition);
} else {
// browser global
global.D3punchcard = moduleDefinition(global.d3);
}}(this));
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>d3.punchcard</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" >
<h1>Lesotho Edits by User (13/02/14 - 18/03/14)</h1>
<div id="example" style="max-width:500px"></div>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.3.11/d3.min.js"></script>
<script src="d3.punchcard.js"></script>
<script>
d3.csv(
'LesothoUserChanges.csv',
// parsing function
function (d) {
return d3.entries(d);
},
// callback function
function ( data ) {
showGraph( data );
}
)
function showGraph ( data ) {
var flatAscending,
upperLimit,
examplePunchcard;
flatAscending = data.map( function(array) {
var value;
return array.slice(1).map( function ( sliced ) {
return parseFloat( sliced.value );
}).filter(function ( element ) {
return element > 0;
});
}).reduce(function(a, b) {
return a.concat(b);
}).sort(function(a, b) {
return a - b;
} );
// we find the upper limit quantile in order
// to not show upper outliers
upperLimit = d3.quantile( flatAscending, 0.95 );
examplePunchcard = new D3punchcard({
data: data,
element: '#example',
upperLimit: upperLimit
})
.draw({ width: document.getElementById('example').offsetWidth });
}
</script>
</body>
</html>
user mon tue wed thu fri sat sun total
4rch 0 0 0 9.0 0 0 0 9.0
AE35 11.0 4.0 1.0 3.0 1.0 1.0 1.0 22.0
Adrian Frith 0 0 1.0 0 0 1.0 0 2.0
Alecs01 0 0 0 0 0 1.0 0 1.0
Alex24223 0 1.0 0 0 0 0 0 1.0
AnyFile 1.0 0 0 0 0 0 1.0 2.0
AtomLaw 6.0 9.0 7.0 0 0 0 1.0 23.0
Aurimas Fišeras 0 2.0 1.0 0 0 1.0 0 4.0
Bacash 0 0 1.0 0 0 0 0 1.0
BeowulfIRL 0 0 0 0 0 1.0 0 1.0
Boggedy 2.0 0 2.0 2.0 0 0 0 6.0
Bonix-Mapper 1.0 0 0 0 0 0 0 1.0
Bopane 3.0 0 0 0 10.0 0 0 13.0
Bryce C Nesbitt 1.0 0 0 0 0 0 0 1.0
Callum_Robinson 0 4.0 0 0 0 0 0 4.0
Chabior 1.0 1.0 0 0 0 2.0 0 4.0
Chris Viljoen (EWCop) 0 1.0 5.0 0 0 0 0 6.0
ChristineFriel 3.0 0 0 0 0 0 0 3.0
Cian Gargan 2.0 2.0 0 0 1.0 0 0 5.0
CiaranPalmer 0 0 1.0 0 0 0 0 1.0
Cliff Jones zs6bju 0 0 0 0 1.0 1.0 0 2.0
Courtney6 1.0 0 0 0 0 0 0 1.0
Cranners111 0 13.0 0 0 1.0 0 0 14.0
Creando 1.0 0 0 0 0 0 0 1.0
DAHNiall 0 0 1.0 0 0 0 4.0 5.0
DaCor 0 11.0 8.0 6.0 0 1.0 4.0 30.0
David_Ward 2.0 4.0 0 0 0 0 0 6.0
DeBigC 110.0 176.0 68.0 16.0 39.0 231.0 205.0 845.0
Diarmuid Gallagher 6.0 2.0 0 0 0 0 0 8.0
Dymryk 0 0 0 0 0 1.0 0 1.0
EO'Driscoll 2.0 0 0 0 0 0 0 2.0
Edgar zola 1.0 0 10.0 18.0 2.0 0 0 31.0
Enock4seth 1.0 0 0 0 0 0 0 1.0
EoinOrr 15.0 4.0 0 0 0 0 0 19.0
Ernst Poulsen 0 0 0 0 0 1.0 0 1.0
Eóin Holton 4.0 0 0 0 0 0 0 4.0
FVerjans 1.0 0 0 2.0 0 0 0 3.0
Firefishy 2.0 2.0 2.0 1.0 0 2.0 3.0 12.0
FvGordon 2.0 3.0 0 1.0 2.0 6.0 0 14.0
HarryMcLauchlan 0 0 1.0 0 0 0 0 1.0
Hazy1983 6.0 0 0 0 0 1.0 0 7.0
Ithatz 0 15.0 0 1.0 0 0 1.0 17.0
JFK73 1.0 0 0 0 0 0 0 1.0
JMK247W 0 0 0 0 0 1.0 0 1.0
Janie Smith 0 3.0 0 0 0 0 0 3.0
Joe Resort 9.0 1.0 0 0 0 4.0 5.0 19.0
JoeCorr 0 0 0 0 0 1.0 0 1.0
JordanKepler 1.0 8.0 24.0 13.0 3.0 1.0 1.0 51.0
KDDA 0 2.0 0 0 0 2.0 0 4.0
KZawadzki 4.0 2.0 0 0 0 0 0 6.0
Katiso Rachabane 0 4.0 0 0 0 0 0 4.0
Kheekhe 0 0 0 0 3.0 0 0 3.0
Kieran Connor1 0 1.0 0 0 0 0 0 1.0
Lara (EWC) 1.0 2.0 1.0 0 0 0 0 4.0
LarsBG 0 0 0 1.0 0 1.0 1.0 3.0
Latze 2.0 1.0 0 0 0 1.0 0 4.0
Leah 1.0 0 0 0 0 0 0 1.0
LeboM 1.0 0 0 2.0 6.0 0 2.0 11.0
Lehlohonolo 0 0 3.0 1.0 0 0 0 4.0
Lekoro 8.0 8.0 7.0 1.0 0 0 0 24.0
Lineo 12.0 41.0 33.0 1.0 4.0 0 6.0 97.0
Liraneko 0 0 0 0 4.0 0 0 4.0
Little Brother 0 0 0 0 0 0 1.0 1.0
LollyMay 1.0 0 0 0 0 0 1.0 2.0
Luke Purtell 1.0 1.0 0 0 0 0 0 2.0
Léo_M 2.0 0 0 0 0 0 0 2.0
Mabatho 0 0 0 0 14.0 0 0 14.0
Majakathata 7.0 0 0 1.0 0 0 0 8.0
Mampho Shale 3.0 0 0 0 0 0 0 3.0
Manfredolbrich 0 1.0 0 0 0 0 0 1.0
MannyChoudhry 0 0 0 1.0 0 0 0 1.0
MapMakinMeyers 1.0 0 0 0 0 0 0 1.0
MartinaTY 7.0 10.0 0 0 0 1.0 1.0 19.0
Math1985 0 0 0 0 0 1.0 0 1.0
MattOD11 0 0 0 0 0 0 1.0 1.0
Miguel Carrilho 2.0 0 0 0 0 0 0 2.0
Moseme 0 0 3.0 0 0 1.0 0 4.0
Mpai 0 0 0 0 2.0 0 0 2.0
Mpaleng Oliphant 0 0 1.0 0 3.0 0 0 4.0
Murph_Mon 2.0 0 0 0 0 2.0 0 4.0
Nicole Curran 3.0 7.0 0 0 0 0 0 10.0
NoelB 0 0 0 0 0 4.0 4.0 8.0
Nomnomnom 1.0 0 0 0 0 0 0 1.0
Ntate MOKOENA 0 0 0 0 2.0 0 0 2.0
OisinD 0 3.0 0 0 0 0 0 3.0
Papali Leboela 2.0 0 0 0 0 0 0 2.0
Patrick Ferry 2.0 2.0 0 0 0 0 1.0 5.0
Patricka 1.0 0 2.0 0 0 1.0 1.0 5.0
Paul Mallet 0 0 0 0 0 0 1.0 1.0
Qoqolosi 0 1.0 0 0 0 0 0 1.0
R0bst3r 0 1.0 0 0 0 0 0 1.0
RAytoun 8.0 0 1.0 0 5.0 1.0 1.0 16.0
Ralph Brennan 1.0 7.0 0 0 0 0 0 8.0
Rbanick 0 1.0 2.0 0 0 0 0 3.0
Rdowd 0 1.0 0 0 0 0 0 1.0
ReinisLo 1.0 2.0 0 0 0 0 8.0 11.0
Rezane van Vuuren (EWCop) 0 1.0 0 0 0 0 0 1.0
RicoElectrico 1.0 0 0 0 0 0 0 1.0
Rovastar 2.0 8.0 0 1.0 0 12.0 6.0 29.0
Rps333 9.0 7.0 0 4.0 0 12.0 1.0 33.0
RustyB 5.0 13.0 1.0 5.0 16.0 24.0 3.0 67.0
SJWoolford 1.0 0 0 0 0 0 0 1.0
SK53 2.0 0 0 0 0 0 1.0 3.0
Seitebatso Mohlahatsa 4.0 3.0 0 1.0 8.0 2.0 2.0 20.0
Shawn Day 3.0 2.0 20.0 0 0 3.0 2.0 30.0
Sonny Andrews 5.0 2.0 0 0 0 0 0 7.0
Stefanoodle 1.0 1.0 0 1.0 0 0 1.0 4.0
Steven_Boyd 0 0 0 0 0 1.0 0 1.0
Stratos P 0 1.0 0 0 0 0 0 1.0
Sundance 1.0 1.0 0 1.0 0 0 0 3.0
T_9er 0 0 1.0 0 0 0 0 1.0
The Big C 12.0 8.0 22.0 11.0 0 9.0 7.0 69.0
The Big D 123 4.0 0 12.0 0 0 0 0 16.0
Tlalane 0 0 1.0 0 0 0 0 1.0
TopsQn 2.0 0 0 1.0 1.0 0 0 4.0
TrevorCB 1.0 0 0 0 0 0 0 1.0
Tsholo 3.0 0 0 0 4.0 0 4.0 11.0
Vincent de Phily 0 0 0 0 0 1.0 0 1.0
WillGuill$$ 0 0 0 0 0 4.0 0 4.0
ailishb 2.0 0 0 0 0 0 0 2.0
amyrogan 0 1.0 0 0 0 0 0 1.0
antwob 0 0 1.0 0 0 0 0 1.0
aoifemcg 6.0 0 0 0 0 0 0 6.0
aschetelig 0 0 0 0 0 1.0 0 1.0
ayoyeni 0 0 1.0 0 0 10.0 0 11.0
bdiscoe 12.0 3.0 2.0 10.0 13.0 2.0 2.0 44.0
bjoernchr74 0 0 0 0 0 1.0 0 1.0
bonnabrand 0 0 0 0 0 1.0 0 1.0
caradevlin 2.0 0 0 0 0 0 5.0 7.0
carciofo 1.0 0 0 0 0 0 0 1.0
ciaraodonnell 2.0 0 0 0 0 0 0 2.0
clement kclt 0 0 2.0 0 3.0 0 0 5.0
conor casey 1.0 0 0 0 0 0 0 1.0
crabbers99 0 2.0 0 0 0 0 0 2.0
cramsay10 0 0 0 1.0 0 0 0 1.0
dallob 1.0 0 12.0 0 2.0 4.0 0 19.0
demimcveigh 6.0 0 0 0 0 0 0 6.0
eireidium 124.0 56.0 18.0 22.0 17.0 19.0 56.0 312.0
eltonp 1.0 1.0 0 0 0 0 0 2.0
emmadouglas 0 1.0 0 0 0 0 0 1.0
eul 0 0 0 0 0 0 1.0 1.0
fifiQn 0 0 0 0 10.0 0 0 10.0
fx99 1.0 1.0 1.0 0 0 0 0 3.0
gisteacher 6.0 0 0 0 0 0 1.0 7.0
gracekennyburke 0 3.0 0 0 0 0 0 3.0
haileydmcclure 0 0 3.0 0 0 0 0 3.0
james_mclaughlin 3.0 0 0 0 0 0 0 3.0
janoelh 1.0 0 1.0 1.0 0 0 0 3.0
jbontes 1.0 1.0 1.0 1.0 0 1.0 0 5.0
joshua_carr 0 2.0 0 0 0 0 0 2.0
kanny 0 0 0 0 7.0 0 0 7.0
katie swords 8.0 0 0 0 0 0 0 8.0
katpatuka 4.0 2.0 0 3.0 0 2.0 0 11.0
kenziemccurdy 0 1.0 0 0 0 0 0 1.0
laurencoyle 0 1.0 0 0 0 0 0 1.0
liteboho 1.0 1.0 0 0 1.0 0 0 3.0
lobstaaa 2.0 0 0 0 0 0 0 2.0
mackerski 0 0 1.0 0 0 1.0 0 2.0
malo98 0 0 0 0 0 2.0 0 2.0
manobby 2.0 0 0 0 37.0 0 0 39.0
mariemallon 2.0 0 0 0 0 0 0 2.0
mateboho 0 0 0 0 1.0 0 0 1.0
mathelvo 0 0 0 0 9.0 0 0 9.0
mavusana 3.0 0 0 0 0 0 0 3.0
mdk 4.0 3.0 3.0 2.0 4.0 6.0 11.0 33.0
morganK 1.0 0 0 0 0 1.0 0 2.0
msdess 0 5.0 0 2.0 0 0 5.0 12.0
nadiaa0618 0 0 1.0 0 0 0 0 1.0
nickyqn 12.0 0 0 19.0 90.0 0 0 121.0
nishadanielle 2.0 6.0 0 0 0 0 0 8.0
nkazo 0 0 0 0 1.0 0 0 1.0
noel1 1.0 0 0 0 0 0 0 1.0
notinblue43 3.0 4.0 0 0 1.0 0 1.0 9.0
ntaote 0 0 0 0 0 0 1.0 1.0
ntebo 0 0 0 0 3.0 0 0 3.0
nuts2001 6.0 1.0 0 2.0 5.0 7.0 1.0 22.0
nyampire 0 1.0 0 0 0 0 0 1.0
pedrito1414 0 2.0 0 0 0 0 0 2.0
petermcloughlin 0 0 0 1.0 0 0 1.0 2.0
philipleonard1234 2.0 0 3.0 0 0 7.0 0 12.0
prfnv 0 0 0 0 0 7.0 2.0 9.0
przemas75 0 0 1.0 1.0 1.0 0 0 3.0
reg111 1.0 0 0 0 0 0 0 1.0
rippunzel 1.0 3.0 1.0 3.0 0 0 0 8.0
rjhale1971 2.0 2.0 1.0 0 0 2.0 0 7.0
roaven 0 0 0 0 0 1.0 0 1.0
rorym 0 0 0 1.0 0 3.0 0 4.0
rpg911 0 2.0 6.0 7.0 0 0 0 15.0
sbarx 1.0 0 0 0 0 0 0 1.0
seisa 2.0 0 0 0 3.0 0 0 5.0
skorasaurus 0 0 0 0 1.0 0 0 1.0
stephen1999 0 1.0 0 0 0 0 0 1.0
stilatila1 0 0 0 0 1.0 0 0 1.0
tdamane 0 0 0 0 1.0 0 0 1.0
teboho1702 129.0 18.0 5.0 41.0 15.0 0 0 208.0
terri ferry 1.0 0 0 0 0 0 0 1.0
the_knife 0 0 0 0 0 1.0 0 1.0
thomasF 1.0 0 0 0 0 0 0 1.0
tmonale 0 0 0 0 13.0 0 0 13.0
triazone 3.0 1.0 1.0 0 0 4.0 0 9.0
triciadoherty 2.0 0 0 0 0 0 0 2.0
trolleway 0 0 0 0 0 1.0 0 1.0
tsebo 0 0 1.0 0 0 0 0 1.0
tshedy 11.0 0 0 0 6.0 0 30.0 47.0
tumie 4.0 0 0 0 10.0 0 0 14.0
unbeZAhlbar 0 3.0 1.0 1.0 1.0 3.0 6.0 15.0
wandsecacher 0 0 0 0 1.0 0 0 1.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment