Skip to content

Instantly share code, notes, and snippets.

@zanarmstrong
Last active April 22, 2022 07:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zanarmstrong/f7793345e15b0916d353ee33c54478c4 to your computer and use it in GitHub Desktop.
Save zanarmstrong/f7793345e15b0916d353ee33c54478c4 to your computer and use it in GitHub Desktop.
comet chart - input your own data
body {
font-family: sans-serif;
}
.segments polygon {
shape-rendering: geometricPrecision;
}
.axis path {
stroke: grey;
stroke-width: 1px;
}
.axis line {
fill: none;
stroke: #727272;
shape-rendering: crispEdges;
stroke-width: .2;
}
.axis text {
font-size: 11px;
fill: #727272;
}
.label {
fill: grey;
stroke: none;
font-size: 16px;
}
// define variables
var margin = {
top: 0,
right: 20,
bottom: 50,
left: 70
},
width = 500,
height = 400;
var formatAsNumber = d3.format(",.0f")
var segmentName = 'birthweight';
// standard insertion of svg element
var svg = d3.select("#chart")
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
var segments = svg.append("g")
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr("class", "segments");
// add labels
segments.append("text")
.attr("class", "x label")
.attr("x", width / 2)
.attr("y", height + 40)
.style("text-anchor", "middle")
.text(mapping.titleWeight);
segments.append("text")
.attr("transform", "rotate(-90)")
.attr("class", "y label")
.attr("x", -height / 2)
.attr("y", -40)
.style("text-anchor", "middle")
.text(mapping.titleValue);
// call and use data
d3.csv("data.csv", function(error, data) {
if (error) return console.error('error');
// filter by filter category, if desired
data = data.filter(function(d) {
if (mapping.filterCat != '') {
return d[mapping.filterName] == mapping.filterCat;
} else {
return d;
}
});
// set scales
scales = applyData(data);
// add the comets
drawComets(segments, data);
// adding x axis, and y axis
addAxis(segments, scales.size, 'bottom');
addAxis(segments, scales.value, 'left')
});
// graph elements
function addAxis(element, scale, location) {
var axis = d3.svg.axis()
.scale(scale)
.orient(location)
.ticks(15, ",.1s")
.tickSize(3, 0);
element.append("g").attr("class", "axis")
.attr("transform", placeAxis(location))
.call(axis);
};
function placeAxis(location) {
if (location == 'bottom') {
return "translate(0," + height + ")";
} else if (location == 'left') {
return "translate(0,0)";
}
}
function applyData(data) {
// initialize counter variables
var maxValue = 0,
maxWeight = 0,
minValue = data[0].startvalue,
minWeight = data[0].startweight,
diff = 0,
sizeSum = [0, 0],
comboSum = [0, 0];
// use data to define max value, weight, weight diffs, and sum size and combo
data.forEach(function(d) {
d.weightDiff = +d.endweight - +d.startweight;
if (Math.abs(d.weightDiff) > diff) {
diff = Math.abs(d.weightDiff)
};
maxValue = Math.max(maxValue, +d.startvalue, +d.endvalue);
maxWeight = Math.max(maxWeight, +d.startweight, +d.endweight);
minValue = Math.min(minValue, +d.startvalue, +d.endvalue);
minWeight = Math.min(minWeight, +d.startweight, +d.endweight);
// calculate for aggregate
sizeSum = [sizeSum[0] + +d.startweight,
sizeSum[1] + +d.endweight
];
comboSum = [comboSum[0] + +d.startweight * +d.startvalue,
comboSum[1] + +d.endweight * +d.endvalue
];
})
// calculate and append aggregate data
// assumes same number of segments for start & end
// assumes no missing values
var aggregate = {
startvalue: comboSum[0] / sizeSum[0],
endvalue: comboSum[1] / sizeSum[1],
startweight: sizeSum[0] / data.length,
endweight: sizeSum[1] / data.length,
};
aggregate[mapping.segmentName] = 'aggregate';
data.push(aggregate);
// sets x and y scale to determine size of visible boxes
if (scaleOptions.weightScale == 'log') {
var sizeScale = d3.scale.log().clamp(true)
.domain([minWeight * .8, maxWeight * 2])
.range([0, width]);
} else {
var sizeScale = d3.scale.linear().clamp(true)
.domain([minWeight * .8, maxWeight * 1.1])
.range([0, width]);
}
if (scaleOptions.valueScale == 'log') {
var valueScale = d3.scale.log().clamp(true)
.domain([minValue * .9, maxValue * 2])
.range([height, 0]);
} else {
var valueScale = d3.scale.linear().clamp(true)
.domain([minValue * .9, maxValue * 1.1])
.range([height, 0]);
}
// color scale, based on data diffs
var colorScale = d3.scale.linear()
.domain([-diff, 0, diff])
.range(['orange', 'grey', 'blue'])
return {
size: sizeScale,
value: valueScale,
color: colorScale
}
};
// data to polygons
function valuesToPoints(startweight, endweight, startvalue, endvalue, halfWidth) {
points = [
[startweight, startvalue]
];
var a = startweight - endweight;
var b = startvalue - endvalue;
var dist = Math.sqrt(a * a + b * b);
var newPoint1 = [halfWidth / dist * b + endweight, -halfWidth / dist * a + endvalue];
var newPoint2 = [-halfWidth / dist * b + endweight, halfWidth / dist * a + endvalue];
points.push(newPoint1, newPoint2);
return points.join(" ")
};
// function to draw comets
function drawComets(element, data) {
element.selectAll("polygon")
.data(data)
.enter()
.append("polygon")
.attr("points", function(d) {
return valuesToPoints(scales.size(+d.startweight),
scales.size(+d.endweight),
scales.value(+d.startvalue),
scales.value(+d.endvalue),
3);
})
.attr("fill", function(d) {
if (d[mapping.segmentName] == 'aggregate') {
//
return 'black';
} else {
return scales.color(d.weightDiff)
};
})
.append("title")
.text(function(d) {
return d[mapping.filterName] +
', ' + mapping.segmentName +
d[mapping.segmentName] +
', value: ' +
d.startvalue + ', ' +
d.endvalue +
' weights ' +
d.startweight + ' ' +
d.endweight;
});
};
state birthweight startvalue endvalue startweight endweight
Ohio 2500 - 2999 grams 5.53 4.88 101227 109151
Ohio 1500 - 1999 grams 29.19 26.96 9078 9904
Ohio 1000 - 1499 grams 67.39 59.15 4526 5038
Ohio 4000+ grams 1.96 1.24 65199 46140
Ohio 500 - 999 grams 336.39 287.95 3377 3445
Ohio 2000 - 2499 grams 13.69 11.62 28782 30884
Ohio 3000 - 3499 grams 3.02 2.45 219696 222982
Ohio 499 grams or less 921.9 901.15 1114 1305
Ohio 3500 - 3999 grams 2.09 1.61 177442 154742
Georgia 2500 - 2999 grams 5.39 4.11 84094 117194
Georgia 1500 - 1999 grams 31.75 25.06 7748 10377
Georgia 1000 - 1499 grams 66.49 59.75 3880 5021
Georgia 4000+ grams 2.03 1.67 42755 35975
Georgia 500 - 999 grams 349.31 293.52 3338 4228
Georgia 2000 - 2499 grams 14.22 10.39 24539 34175
Georgia 3000 - 3499 grams 2.66 2.14 172853 227039
Georgia 499 grams or less 904.81 728.06 956 1151
Georgia 3500 - 3999 grams 2.15 1.63 126734 137645
New Jersey 2500 - 2999 grams 3.93 2.36 74847 82086
New Jersey 1500 - 1999 grams 20.08 16.37 7120 7513
New Jersey 1000 - 1499 grams 56.28 35.94 3536 3645
New Jersey 4000+ grams 1.22 1.09 48311 33939
New Jersey 500 - 999 grams 297.12 269.42 2955 2639
New Jersey 2000 - 2499 grams 9.97 6.84 21156 22957
New Jersey 3000 - 3499 grams 1.95 1.5 165760 173854
New Jersey 499 grams or less 875.33 870.97 762 651
New Jersey 3500 - 3999 grams 1.23 0.99 132117 118722
Florida 2500 - 2999 grams 4.55 3.87 132288 177337
Florida 1500 - 1999 grams 29.05 25.01 11601 15155
Florida 1000 - 1499 grams 56.2 53.75 5783 7275
Florida 4000+ grams 1.81 1.59 70266 58431
Florida 500 - 999 grams 319.63 290.92 4696 5871
Florida 2000 - 2499 grams 12.55 10.32 37381 49210
Florida 3000 - 3499 grams 2.52 1.92 288298 364768
Florida 499 grams or less 911.6 911.06 1233 1664
Florida 3500 - 3999 grams 1.63 1.32 214462 226844
Michigan 2500 - 2999 grams 5.87 3.93 84729 83716
Michigan 1500 - 1999 grams 28.38 31.25 8281 7776
Michigan 1000 - 1499 grams 71.52 55.03 3929 3816
Michigan 4000+ grams 2.04 1.67 59301 41855
Michigan 500 - 999 grams 347.11 295.4 3218 3084
Michigan 2000 - 2499 grams 13.07 9.61 25024 24753
Michigan 3000 - 3499 grams 2.63 2.22 190941 179909
Michigan 499 grams or less 891.05 870.88 1028 1092
Michigan 3500 - 3999 grams 1.91 1.62 158394 132157
Texas 3000 - 3499 grams 2.54 2.06 514828 653989
Texas 1000 - 1499 grams 61.69 53.48 8753 12079
Texas 2500 - 2999 grams 4.55 3.83 226968 320675
Texas 1500 - 1999 grams 30.92 28.16 18695 26033
Texas 4500+ grams 2.23 2.37 17513 12673
Texas 4000 - 4499 grams 1.68 1.67 99225 86139
Texas 499 grams or less 703.34 708.93 1409 2642
Texas 3500 - 3999 grams 1.87 1.4 373838 392317
Texas 500 - 999 grams 308.81 265.38 7163 8859
Texas 2000 - 2499 grams 13.27 10.76 60726 85758
New York 2500 - 2999 grams 4.3 2.78 176330 184645
New York 1500 - 1999 grams 25.99 20.74 16082 16300
New York 1000 - 1499 grams 56.79 46.98 7906 7918
New York 4000+ grams 1.57 1.07 106329 77806
New York 500 - 999 grams 338.98 273.29 6552 5840
New York 2000 - 2499 grams 10.92 7.88 49348 50356
New York 3000 - 3499 grams 2.07 1.45 389495 388484
New York 499 grams or less 931.61 927.12 1506 1276
New York 3500 - 3999 grams 1.43 1.12 296709 263005
Illinois 2500 - 2999 grams 5.75 3.78 121146 125074
Illinois 1500 - 1999 grams 31.83 24.81 11436 11487
Illinois 1000 - 1499 grams 70.31 51.46 5504 5344
Illinois 4000+ grams 1.77 1.37 74666 54564
Illinois 500 - 999 grams 364.81 300.19 4490 4204
Illinois 2000 - 2499 grams 14.06 10.09 35357 35871
Illinois 3000 - 3499 grams 3.11 1.96 268166 268672
Illinois 499 grams or less 901.83 878.87 1477 1453
Illinois 3500 - 3999 grams 2.04 1.36 210021 187259
Pennsylvania 2500 - 2999 grams 4.8 4.51 94587 104298
Pennsylvania 1500 - 1999 grams 30.89 23.89 8644 9711
Pennsylvania 1000 - 1499 grams 63.76 51.12 4203 4851
Pennsylvania 4000+ grams 1.82 1.64 64457 50705
Pennsylvania 500 - 999 grams 326.86 285.59 3298 3456
Pennsylvania 2000 - 2499 grams 12.32 10.13 27202 30112
Pennsylvania 3000 - 3499 grams 2.51 2.19 212567 222310
Pennsylvania 499 grams or less 920.35 918.74 1130 1366
Pennsylvania 3500 - 3999 grams 1.69 1.38 174075 162801
California 3000 - 3499 grams 2.33 1.57 802668 855510
California 1000 - 1499 grams 68.98 54.7 12351 13237
California 2500 - 2999 grams 4.6 3.13 331059 377355
California 1500 - 1999 grams 32.57 24.98 25636 28865
California 4500+ grams 2.37 2.12 36300 24560
California 4000 - 4499 grams 1.67 1.12 194276 153362
California 499 grams or less 879.21 850.28 2318 2518
California 3500 - 3999 grams 1.7 1.21 642323 597310
California 500 - 999 grams 338.35 295.25 8967 9338
California 2000 - 2499 grams 12.98 9.85 81989 93270
<!DOCTYPE html>
<meta charset="utf-8">
<title>Comet Chart</title>
<link rel="stylesheet" href="cometChart.css">
<header>
</header>
<p>To use this chart with your own data:</p>
<p>(1) paste your data into the data.csv</p>
<p>(2) change the column names in the mappingNames.js file</p>
<p>(optional) set the scale for each axis to log or linear</p>
<div id="chart"></div>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="mappingNames.js"></script>
<script src="cometChartFunctions.js"></script>
<script src="cometChart.js"></script>
var mapping = {
// columns names for "value" metrics (y-axis), and a title for the size/weight value to use on the y-axis
startValue: "deathRate_before",
endValue: "deathRate_after",
titleValue: "Fetal Death Rate",
// column names for "weight" metrics (x-axis), and a title for the size/weight variable to use on the x-axis
startWeight: "numBabiesBorn_before",
endWeight: "numBabiesBorn_after",
titleWeight: "Number of Babies Born",
// column to segment the data into subgroups
segmentName: "birthweight",
// if you want to filter by another variable, type the name of that column here - and the category you want to filter as 'filterCat', as shown in the options that are commented out
//filterName: "state",
//filterCat: "Ohio"
}
var scaleOptions = {
// set scale (either 'linear' or 'log')
weightScale: "log",
valueScale: "log"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment