Skip to content

Instantly share code, notes, and snippets.

@galvanic
Last active July 6, 2016 16:00
Show Gist options
  • Save galvanic/b309059053391f994ba614c0432d7a8c to your computer and use it in GitHub Desktop.
Save galvanic/b309059053391f994ba614c0432d7a8c to your computer and use it in GitHub Desktop.
upset chart implementation

Upset chart example

A simple upset chart implementation, a chart type useful for visualising set intersections, applied to flatmate-purchase assignment data from my onlineshop project.

See it live on bl.ocks

id purchase_id flatmate_id
1 1 2
2 2 1
3 3 2
4 3 1
5 4 2
6 5 2
7 5 1
<!DOCTYPE html>
<html>
<head>
<title>Upset Viz</title>
<link rel='stylesheet' type='text/css' href='style.css'>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js' charset='utf-8'></script>
<script src='./UpsetChart.js'></script>
<script src='./main.js'></script>
</head>
<body>
<div id='vis'></div>
</body>
</html>
'use strict'
var dataFilepath = 'assignment.csv'
d3.csv(dataFilepath, function(loadedData) {
// remodel data for chart's requirements
var numFlatmates = d3.nest()
.key(function(a) { return a.flatmate_id })
.entries(loadedData)
.length
var purchaseAssignments = d3.nest()
.key(function(a) { return a.purchase_id })
.rollup(function(assignments) {
var assignment = new Array(numFlatmates).fill(0)
var assignedFlatmates = assignments.map(function(a) { return a.flatmate_id })
assignedFlatmates.forEach(function(f) {
// flatmate ids start at 1 in the csv
assignment[f-1] = 1
})
return assignment
})
.entries(loadedData)
var dataset = {
numFlatmates: numFlatmates
, values: purchaseAssignments
}
// the chart
var svg = d3.select('div#vis')
.append('svg')
.attr('width', 960)
.attr('height', 500)
function draw(data) {
var rows = svg.selectAll('g.upsetchart')
.data(data, function(d) { return d.key })
rows.enter()
.append('g')
.classed('upsetchart purchase', true)
.attr('transform', function(d, i) {
return 'translate(' + 10 + ',' + (10 + i*30) + ')'
})
rows
.call(UpsetChart())
rows.exit()
.remove()
}
draw(dataset.values)
})
div#vis {
width: 960px;
margin: 0px auto;
}
.innerchart line {
stroke: gray;
stroke-width: 4px;
}
circle {
stroke: gray;
stroke-width: 3px;
}
circle.assigned {
fill: gray;
}
circle.not-assigned {
fill: white;
}
function UpsetChart() {
// assumptions about the data passed to this chart:
// i.e. dataset of each selection
// Object with the following keys:
// - key: purchase id
// - values: flatmates assigned to this purchase
// chart config DEFAULTS
var margin = {top: 10, right: 10, bottom: 10, left: 10}
var width = 960 - margin.left - margin.right
var height = 30 - margin.top - margin.bottom
function drawChart(selection) {
selection.each(function(dataset, i) {
var wholeChart = this
//
// THE CHART
//
// select chart element(s) (eg. innerchart) if they exist already
var innerchart = d3.select(wholeChart).selectAll('g.innerchart')
.data(function(dataset) { return [dataset.values] })
// otherwise create them and the elements they contain
var innerchartEnter = innerchart.enter()
.append('g')
.classed('innerchart', true)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
innerchartEnter
.append('line')
.attr('x1', 10)
.attr('y1', 10)
.attr('x2', 60-10-10)
.attr('y2', 10)
// update data
var flatmates = innerchart.selectAll('circle')
.data(function(d) { return d })
flatmates.enter()
.append('circle')
.attr('class', function(d, i) {
var assigned
if (d) { assigned = 'assigned' }
else { assigned = 'not-assigned' }
var flatmate_id = i+1
return assigned + ' ' + flatmate_id
})
.attr('cx', function(d, i) { return 10+i*(10+20) })
.attr('cy', 10)
.attr('r', 10)
})
}
//
// Chart CONFIG getter setter methods
//
// TODO figure out which side effects are actually necessary to recalculate
drawChart.width = function(value) {
if (!arguments.length) return width
width = value
return drawChart
}
drawChart.height = function(value) {
if (!arguments.length) return height
height = value
return drawChart
}
return drawChart
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment