Skip to content

Instantly share code, notes, and snippets.

@ndobie
Last active January 13, 2017 14:31
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 ndobie/878f34d2810058c5b821 to your computer and use it in GitHub Desktop.
Save ndobie/878f34d2810058c5b821 to your computer and use it in GitHub Desktop.
Bar Chart and Line Chart on Same D3 Plot
license: mit
border: no

New Mexico Oil & Gas Employment

This visualization shows employment for the oil, gas & mining sector for New Mexico state and counties in 2010 through 2014 including a Y/Y change (bar chart). In addition, the 2014 data shown in the pie chart compares sector employment to total employment in the geography. The goal of this visualization is for the viewer to answer their own questions and draw their own conclusions. Standards of good graph making (as presented by Tufte) have been followed in an attempt to not influence viewers opinions.
What's nice about this code is it is driven off of CSV files. By changing the names of the CSV files in the index.html file, I can change the plots entirely. The math behind it makes the charts adapt given that you zero fill missing geographies. This was a fun plot to make. Not only is their an interactive pie chart showing employment in the Oil & Gas sector, but the graph has 2 different scales going on. Ultimately because of how d3 works, scaling to this to include many more secors or geographies is easy. I created this as a set, one for each 2 digit NAICS industry code.
The data behind this chart is from the quarterly census of employment and wages, a Bureau of Labor Statistics program. I created this for work on a website, BBER.UNM.EDU, check out this and many more over there.
area periodyear estab avgemp totwage avgwkwage changeemp
00 2010 916 18423 1381058529 1442 4.79
00 2011 958 21252 1529138643 1384 15.36
00 2012 1027 23988 1760844239 1412 12.87
00 2013 1074 25998 1914237264 1416 8.38
00 2014 1188 27869 2145469136 1480 7.20
01 2010 26 139 8350128 1155 -2.11
01 2011 27 187 11360139 1168 34.53
01 2012 23 169 10428762 1187 -9.63
01 2013 23 174 10116181 1118 2.96
01 2014 25 119 6754423 1092 -31.61
03 2010 3 5 249797 961 -16.67
03 2011 3 6 193113 619 20.00
03 2012 0 0.00
03 2013 0 0.00
03 2014 0 0.00
05 2010 52 411 19917780 932 8.44
05 2011 52 448 25132007 1079 9.00
05 2012 52 448 25779882 1107 0.00
05 2013 53 563 32536526 1111 25.67
05 2014 55 602 38929171 1244 6.93
06 2010 0 0.00
06 2011 0 0.00
06 2012 0 0.00
06 2013 0 0.00
06 2014 5 446 31489341 1358 0.00
07 2010 0 0.00
07 2011 0 0.00
07 2012 0 0.00
07 2013 0 0.00
07 2014 4 54 3935593 1402 0.00
09 2010 0 0.00
09 2011 0 0.00
09 2012 0 0.00
09 2013 0 0.00
09 2014 0 0.00
11 2010 0 0.00
11 2011 0 0.00
11 2012 0 0.00
11 2013 0 0.00
11 2014 0 0.00
13 2010 8 40 1524690 733 0.00
13 2011 9 45 1826488 781 12.50
13 2012 8 24 975573 782 -46.67
13 2013 7 20 773580 744 -16.67
13 2014 6 16 630501 758 -20.00
15 2010 118 4025 405206292 1936 13.54
15 2011 137 4888 347876576 1369 21.44
15 2012 162 5570 398670279 1376 13.95
15 2013 187 6593 471155997 1374 18.37
15 2014 206 7275 547419268 1447 10.34
17 2010 0 0.00
17 2011 0 0.00
17 2012 0 0.00
17 2013 11 1646 116374995 1360 0.00
17 2014 0 0.00
19 2010 0 0.00
19 2011 0 0.00
19 2012 0 0.00
19 2013 0 0.00
19 2014 0 0.00
21 2010 0 0.00
21 2011 0 0.00
21 2012 0 0.00
21 2013 0 0.00
21 2014 0 0.00
23 2010 0 0.00
23 2011 0 0.00
23 2012 0 0.00
23 2013 0 0.00
23 2014 0 0.00
25 2010 270 5851 377842790 1242 5.35
25 2011 283 6292 440812609 1347 7.54
25 2012 329 7481 535700154 1377 18.90
25 2013 345 8033 573212693 1372 7.38
25 2014 384 8836 662433494 1442 10.00
27 2010 3 18 1109433 1185 -10.00
27 2011 0 0.00
27 2012 0 0.00
27 2013 0 0.00
27 2014 0 0.00
28 2010 0 0.00
28 2011 0 0.00
28 2012 0 0.00
28 2013 0 0.00
28 2014 0 0.00
29 2010 0 0.00
29 2011 0 0.00
29 2012 0 0.00
29 2013 0 0.00
29 2014 3 15 408673 524 0.00
31 2010 7 75 6588209 1689 -36.44
31 2011 7 76 6892132 1744 1.33
31 2012 7 121 11213626 1782 59.21
31 2013 7 72 6529965 1744 -40.50
31 2014 7 37 3619322 1881 -48.61
33 2010 0 0.00
33 2011 0 0.00
33 2012 0 0.00
33 2013 0 0.00
33 2014 0 0.00
35 2010 0 0.00
35 2011 0 0.00
35 2012 0 0.00
35 2013 0 0.00
35 2014 6 74 2730057 709 0.00
37 2010 0 0.00
37 2011 0 0.00
37 2012 0 0.00
37 2013 0 0.00
37 2014 0 0.00
39 2010 13 76 3807266 963 10.14
39 2011 11 83 4056069 940 9.21
39 2012 9 86 4148650 928 3.61
39 2013 8 91 4338278 917 5.81
39 2014 7 52 1440259 533 -42.86
41 2010 0 0.00
41 2011 0 0.00
41 2012 0 0.00
41 2013 0 0.00
41 2014 0 0.00
43 2010 10 53 2264269 822 6.00
43 2011 11 73 2827383 745 37.74
43 2012 13 95 3685119 746 30.14
43 2013 13 94 3870144 792 -1.05
43 2014 14 90 4030756 861 -4.26
45 2010 248 5548 414291854 1436 2.59
45 2011 251 6409 504661314 1514 15.52
45 2012 239 6470 524022680 1558 0.95
45 2013 231 6480 529648995 1572 0.15
45 2014 241 6723 563723829 1613 3.75
47 2010 0 0.00
47 2011 0 0.00
47 2012 0 0.00
47 2013 5 22 572905 501 0.00
47 2014 4 10 237123 456 -54.55
49 2010 18 101 6747529 1285 -15.83
49 2011 20 92 6228356 1302 -8.91
49 2012 21 99 7112137 1382 7.61
49 2013 18 86 7141692 1597 -13.13
49 2014 21 102 7446901 1404 18.60
51 2010 4 6 390008 1250 0.00
51 2011 0 0.00
51 2012 0 0.00
51 2013 0 0.00
51 2014 0 0.00
53 2010 0 0.00
53 2011 0 0.00
53 2012 0 0.00
53 2013 0 0.00
53 2014 0 0.00
55 2010 7 337 19292607 1101 -30.52
55 2011 7 347 22112640 1225 2.97
55 2012 6 301 21094605 1348 -13.26
55 2013 0 0.00
55 2014 5 228 19689579 1661 0.00
57 2010 0 0.00
57 2011 0 0.00
57 2012 0 0.00
57 2013 0 0.00
57 2014 0 0.00
59 2010 0 0.00
59 2011 0 0.00
59 2012 0 0.00
59 2013 0 0.00
59 2014 0 0.00
61 2010 6 28 1287246 884 -31.71
61 2011 6 30 1513148 970 7.14
61 2012 6 29 1398569 927 -3.33
61 2013 0 0.00
61 2014 5 16 924566 1111 0.00
area periodyear estab avgemp totwage avgwkwage changeemp
00 2010 403 6174 380314037 1185 -4.23
00 2011 404 6154 384215331 1201 -0.32
00 2012 400 6179 393294767 1224 0.41
00 2013 396 6238 405472897 1250 0.95
00 2014 398 6219 416584266 1288 -0.30
01 2010 23 733 51099874 1341 -4.81
01 2011 24 770 55671311 1390 5.05
01 2012 23 794 61887324 1499 3.12
01 2013 24 893 73266202 1578 12.47
01 2014 25 910 77837588 1645 1.90
03 2010 4 10 358983 690 0.00
03 2011 4 9 322175 688 -10.00
03 2012 4 11 387893 678 22.22
03 2013 4 12 395339 634 9.09
03 2014 4 11 397367 695 -8.33
05 2010 11 141 6604817 901 -2.76
05 2011 11 142 6889488 933 0.71
05 2012 12 149 7152809 923 4.93
05 2013 12 151 7628634 972 1.34
05 2014 13 127 6135289 929 -15.89
06 2010 7 223 10671235 920 0.00
06 2011 0 0.00
06 2012 0 0.00
06 2013 0 0.00
06 2014 8 246 13244250 1035 0.00
07 2010 12 74 3285915 854 2.78
07 2011 12 74 3255606 846 0.00
07 2012 11 68 3017443 853 -8.11
07 2013 12 78 3795147 936 14.71
07 2014 12 76 3980833 1007 -2.56
09 2010 11 129 7178190 1070 1.57
09 2011 10 130 7338655 1086 0.78
09 2012 10 132 7592147 1106 1.54
09 2013 10 135 6886167 981 2.27
09 2014 9 126 6433508 982 -6.67
11 2010 4 26 798349 590 8.33
11 2011 4 30 833491 534 15.38
11 2012 4 30 863250 553 0.00
11 2013 4 27 684974 488 -10.00
11 2014 4 28 781151 537 3.70
13 2010 38 659 35756903 1043 -1.93
13 2011 36 668 35847581 1032 1.37
13 2012 36 707 38094134 1036 5.84
13 2013 34 719 38853810 1039 1.70
13 2014 33 725 38959907 1033 0.83
15 2010 24 285 14142404 954 4.01
15 2011 26 300 15314572 982 5.26
15 2012 26 297 16111523 1043 -1.00
15 2013 26 308 17618897 1100 3.70
15 2014 27 344 20874726 1167 11.69
17 2010 13 101 4880999 929 -3.81
17 2011 13 99 4945310 961 -1.98
17 2012 12 98 4905376 963 -1.01
17 2013 12 96 4784721 958 -2.04
17 2014 12 96 5005205 1003 0.00
19 2010 0 0.00
19 2011 0 0.00
19 2012 0 0.00
19 2013 0 0.00
19 2014 5 22 591543 517 0.00
21 2010 0 0.00
21 2011 0 0.00
21 2012 0 0.00
21 2013 0 0.00
21 2014 0 0.00
23 2010 5 13 813847 1204 8.33
23 2011 5 17 966883 1094 30.77
23 2012 5 18 1080191 1154 5.88
23 2013 5 15 946378 1213 -16.67
23 2014 5 12 886985 1421 -20.00
25 2010 25 393 25363353 1241 8.56
25 2011 22 403 27053129 1291 2.54
25 2012 21 406 28438640 1347 0.74
25 2013 21 412 29612710 1382 1.48
25 2014 22 422 31014820 1413 2.43
27 2010 12 116 5446498 903 16.00
27 2011 13 109 5266919 929 -6.03
27 2012 13 113 5763341 981 3.67
27 2013 13 117 5811443 955 3.54
27 2014 13 121 6164420 980 3.42
28 2010 4 91 4881789 1032 -1.09
28 2011 4 93 5069969 1048 2.20
28 2012 4 91 5034791 1064 -2.15
28 2013 4 94 5090106 1041 3.30
28 2014 4 93 5129947 1061 -1.06
29 2010 0 0.00
29 2011 0 0.00
29 2012 0 0.00
29 2013 6 65 3415701 1011 0.00
29 2014 6 67 4090930 1174 3.08
31 2010 14 225 14482579 1238 -0.88
31 2011 13 212 13904082 1261 -5.78
31 2012 14 211 14323044 1305 -0.47
31 2013 14 206 14108387 1317 -2.37
31 2014 13 205 15444464 1449 -0.49
33 2010 0 0.00
33 2011 0 0.00
33 2012 0 0.00
33 2013 0 0.00
33 2014 3 26 1274836 943 0.00
35 2010 18 142 6807092 922 -0.70
35 2011 18 145 7062324 937 2.11
35 2012 17 143 7025218 945 -1.38
35 2013 19 158 7493739 912 10.49
35 2014 18 157 7902158 968 -0.63
37 2010 10 60 2480185 795 -4.76
37 2011 10 59 2470322 805 -1.67
37 2012 10 56 2531650 869 -5.08
37 2013 10 56 2303609 791 0.00
37 2014 10 58 2454598 814 3.57
39 2010 15 168 8221822 941 -2.33
39 2011 15 163 8030014 947 -2.98
39 2012 15 155 8234158 1022 -4.91
39 2013 15 147 7706978 1008 -5.16
39 2014 15 131 7100111 1042 -10.88
41 2010 8 67 2702229 776 -2.90
41 2011 7 68 2887617 817 1.49
41 2012 7 70 3139973 863 2.94
41 2013 7 74 3306659 859 5.71
41 2014 8 76 3832170 970 2.70
43 2010 19 148 7447200 968 -2.63
43 2011 20 142 7766002 1052 -4.05
43 2012 17 90 3571897 763 -36.62
43 2013 14 77 3180862 794 -14.44
43 2014 14 89 4232142 914 15.58
45 2010 25 1318 118713434 1732 -14.47
45 2011 25 1247 112081048 1728 -5.39
45 2012 25 1230 111338871 1741 -1.36
45 2013 23 1171 109953201 1806 -4.80
45 2014 24 1115 107861259 1860 -4.78
47 2010 9 81 2619909 622 3.85
47 2011 10 86 2819327 630 6.17
47 2012 11 98 3206020 629 13.95
47 2013 12 107 3023241 543 9.18
47 2014 12 99 2919520 567 -7.48
49 2010 24 265 14620582 1061 -0.75
49 2011 23 256 14936904 1122 -3.40
49 2012 22 252 14959199 1142 -1.56
49 2013 20 247 14985448 1167 -1.98
49 2014 23 254 15388724 1165 2.83
51 2010 0 0.00
51 2011 0 0.00
51 2012 0 0.00
51 2013 6 47 2192504 897 0.00
51 2014 6 45 2103872 899 -4.26
53 2010 0 0.00
53 2011 0 0.00
53 2012 0 0.00
53 2013 0 0.00
53 2014 6 92 3892224 814 0.00
55 2010 13 149 6120835 790 -11.83
55 2011 13 143 5981007 804 -4.03
55 2012 13 152 6239830 789 6.29
55 2013 12 151 6555689 835 -0.66
55 2014 12 151 6578513 838 0.00
57 2010 0 0.00
57 2011 0 0.00
57 2012 0 0.00
57 2013 0 0.00
57 2014 8 100 5624973 1082 0.00
59 2010 5 31 1506168 934 0.00
59 2011 5 30 1560994 1001 -3.23
59 2012 5 30 1631608 1046 0.00
59 2013 5 32 1517004 912 6.67
59 2014 5 31 1559375 967 -3.13
61 2010 15 138 5509286 768 -0.72
61 2011 15 135 5610963 799 -2.17
61 2012 13 149 6067207 783 10.37
61 2013 13 150 6077155 779 0.67
61 2014 13 150 6189882 794 0.00
<!DOCTYPE html>
<html>
<head>
<title>New Mexico Employment in NAICS Sector 21</title>
<meta charset="utf-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<style>
@import url('//fonts.googleapis.com/css?family=Lato:400,400italic,700,700italic');
body {
font-family: 'Lato', Arial, Helvetica, sans-serif;
font-size: 16px;
position: relative;
}
h3 {
margin-left: 4px;
}
.arc text {
text-anchor: middle;
}
.arc path {
stroke: #fff;
}
#pie-chart {
background-color: #ffffff;
height: 370px;
text-shadow: none;
margin-right: 0;
}
#pie-chart .total {
font-size: 18px;
font-weight: bold;
}
#pie-chart .units {
fill: gray;
font-size: 12px;
}
#pie-chart .label {
fill: #CCC;
font-size: 12px;
font-weight: bold;
}
#pie-chart .value {
font-size: 18px;
font-weight: bold;
}
.toolTip {
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
.bar text {
font-size: 1em;
font-weight: 300;
cursor: default;
}
.axis line {
stroke: #AAA;
stroke-width: 1;
}
line.median {
stroke: #777;
stroke-width: 1;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.bar {
fill: #74c476;
}
.bar:hover {
fill: #a1d99b;
}
.axisR {
font: 0.625em sans-serif;
}
.axisL {
font: 0.625em sans-serif;
}
.axis {
font: 0.625em sans-serif;
}
.axisR path,
.axisR line,
.axisL path,
.axisL line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.chartLine {
fill: red;
stroke: red;
}
.chartLineText {
font: 0.625em sans-serif;
pointer-events: none;
}
.barText {
font: 0.7em sans-serif;
font-weight: 500;
color: lightgray;
visibility: hidden;
}
a {
color: dimgray;
}
.source {
font-size: 0.625em;
color: grey;
line-height: 80%;
}
.prepaired {
font-size: 0.625em;
color: grey;
line-height: 80%;
}
.update {
font-size: 0.625em;
color: grey;
line-height: 80%;
}
.notes {
font-size: 0.625em;
color: grey;
line-height: 80%;
}
.def {
font-size: .75em;
color: grey;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<form>
<span style="color:dimgrey" id="title">Oil & Gas + Mining Employment: </span><select id="geo" style="width:200px;">
<option value='00'>New Mexico</option>
<option value='01'>Bernalillo</option>
<option value='03'>Catron</option>
<option value='05'>Chaves</option>
<option value='06'>Cibola</option>
<option value='07'>Colfax</option>
<option disabled value='09'>Curry</option>
<option disabled value='11'>De Baca</option>
<option value='13'>Do&ntilde;a Ana</option>
<option value='15'>Eddy</option>
<option value='17'>Grant</option>
<option disabled value='19'>Guadalupe</option>
<option disabled value='21'>Harding</option>
<option disabled value='23'>Hidalgo</option>
<option value='25'>Lea</option>
<option value='27'>Lincoln</option>
<option disabled value='28'>Los Alamos</option>
<option value='29'>Luna</option>
<option value='31'>Mckinley</option>
<option disabled value='33'>Mora</option>
<option value='35'>Otero</option>
<option disabled value='37'>Quay</option>
<option value='39'>Rio Arriba</option>
<option disabled value='41'>Roosevelt</option>
<option value='43'>Sandoval</option>
<option value='45'>San Juan</option>
<option value='47'>San Miguel</option>
<option value='49'>Santa Fe</option>
<option value='51'>Sierra</option>
<option disabled value='53'>Socorro</option>
<option value='55'>Taos</option>
<option disabled value='57'>Torrance</option>
<option disabled value='59'>Union</option>
<option value='61'>Valencia</option>
</select>
</form>
<div id="wrap" style="max-width:960px;">
<div id="pie-chart" style="float: left; width:510px;margin:0;"></div>
<div id="graph" style="float: left; width:450px;margin:0;"></div>
<span class="prepaired" style='color:grey;font-size:0.625em;'>Prepared by: Nathan Dobie for UNM Bureau of Business and Economic Research</span>
<br />
<span id='source' style='display:block;color:grey;font-size:0.625em;'>Source: Annual 2014 Quarterly Census of Employment and Wages, New Mexico Department of Workforce Solutions. Numbers reflect only persons covered under unemployment insurance, not all employment is covered under unemployment insurance law. Change y/y calculated from data.</span>
</div>
<script type="text/javascript">
d3.select(self.frameElement).attr("height", "480px")
var div = d3.select("body").append("div").attr("class", "toolTip");
var w = 510;
var h = 390;
var r = 130;
var ir = 78;
var textOffset = 24;
var tweenDuration = 1050;
//OBJECTS TO BE POPULATED WITH DATA LATER
var lines, valueLabels, nameLabels;
var pieData = [];
var oldPieData = [];
var filteredPieData = [];
//D3 helper function to populate pie slice parameters from array data
var donut = d3.layout.pie().value(function (d) {
return d.emp;
}).sort(null);
////D3 helper function to create colors from an ordinal scale
//var color = d3.scale.category20c();
var color = d3.scale.ordinal()
.range(['#6baed6', '#aaa']);
//D3 helper function to draw arcs, populates parameter "d" in path object
var arc = d3.svg.arc()
.startAngle(function (d) { return d.startAngle; })
.endAngle(function (d) { return d.endAngle; })
.innerRadius(ir)
.outerRadius(r);
function formatMoney(num) {
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")
};
var vis = d3.select("#pie-chart").append("svg:svg")
.attr("width", w)
.attr("height", h);
//GROUP FOR ARCS/PATHS
var arc_group = vis.append("svg:g")
.attr("class", "arc")
.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");
//GROUP FOR LABELS
var label_group = vis.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");
//GROUP FOR CENTER TEXT
var center_group = vis.append("svg:g")
.attr("class", "center_group")
.attr("transform", "translate(" + (w / 2) + "," + (h / 2) + ")");
//WHITE CIRCLE BEHIND LABELS
var whiteCircle = center_group.append("svg:circle")
.attr("fill", "white")
.attr("r", ir);
function type(d) {
d.wage = +d.wage;
d.emp = +d.emp;
d.gdp = +d.gdp;
return d;
};
d3.csv("sector_21.csv", type, function (error, dataI) {
d3.selectAll("#geo")
.on("change", update);
function update() {
dataI.value = dataI.emp
var I = document.getElementById('geo').selectedIndex
, select = document.getElementById('geo')[I].value;
data = dataI.filter(function (d) { return d.area == select });
oldPieData = filteredPieData;
pieData = donut(data);
var sliceProportion = 0; //size of this slice
filteredPieData = pieData.filter(filterData);
function filterData(element, index, array) {
element.emp = data[index].emp;
element.wage = data[index].wage;
element.ind = data[index].ind;
element.label = data[index].label;
sliceProportion += element.emp;
return (element.wage >= 0)
}
//DRAW ARC PATHS
paths = arc_group.selectAll("path").data(filteredPieData);
paths.enter().append("svg:path")
.attr("stroke", "white")
.attr("stroke-width", 0.5)
.attr("fill", function (d, i) { return color(i); })
.transition()
.duration(tweenDuration)
.attrTween("d", pieTween);
paths
.transition()
.duration(tweenDuration)
.attrTween("d", pieTween);
paths.exit()
.transition()
.duration(tweenDuration)
.attrTween("d", removePieTween)
.remove();
paths.on("mousemove", function (d) {
div.style("left", d3.event.pageX + 10 + "px");
div.style("top", d3.event.pageY - 25 + "px");
div.style("display", "inline-block");
div.html(d.label + ': ' + (d.ind) + "<br>" + 'Covered Employment: ' + (formatMoney(d.emp)));
});
paths.on("mouseout", function (d) {
div.style("display", "none");
});
//DRAW TICK MARK LINES FOR LABELS
lines = label_group.selectAll("line").data(filteredPieData);
lines.enter().append("svg:line")
.attr("x1", 0)
.attr("x2", 0)
.attr("y1", -r - 3)
.attr("y2", -r - 15)
.attr("stroke", "gray")
.attr("transform", function (d) {
return "rotate(" + (d.startAngle + d.endAngle) / 2 * (180 / Math.PI) + ")";
});
lines.transition()
.duration(tweenDuration)
.attr("transform", function (d) {
return "rotate(" + (d.startAngle + d.endAngle) / 2 * (180 / Math.PI) + ")";
});
lines.exit().remove();
//DRAW LABELS WITH PERCENTAGE VALUES
valueLabels = label_group.selectAll("text.value").data(filteredPieData)
.attr("dy", function (d) {
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
return 5;
} else {
return -7;
}
})
.attr("text-anchor", function (d) {
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
return "beginning";
} else {
return "end";
}
})
.text(function (d) {
var percentage = (d.value / sliceProportion) * 100;
return percentage.toFixed(1) + "%";
});
valueLabels.enter().append("svg:text")
.attr("class", "value")
.attr("transform", function (d) {
return "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) * (r + textOffset) + "," + Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) * (r + textOffset) + ")";
})
.attr("dy", function (d) {
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
return 5;
} else {
return -7;
}
})
.attr("text-anchor", function (d) {
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
return "beginning";
} else {
return "end";
}
}).text(function (d) {
var percentage = (d.value / sliceProportion) * 100;
return percentage.toFixed(1) + "%";
});
valueLabels.transition().duration(tweenDuration).attrTween("transform", textTween);
valueLabels.exit().remove();
//DRAW LABELS WITH ENTITY NAMES
nameLabels = label_group.selectAll("text.units").data(filteredPieData)
.attr("dy", function (d) {
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
return 17;
} else {
return 5;
}
})
.attr("text-anchor", function (d) {
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
return "beginning";
} else {
return "end";
}
}).text(function (d) {
return d.ind;
});
nameLabels.enter().append("svg:text")
.attr("class", "units")
.attr("transform", function (d) {
return "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) * (r + textOffset) + "," + Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) * (r + textOffset) + ")";
})
.attr("dy", function (d) {
if ((d.startAngle + d.endAngle) / 2 > Math.PI / 2 && (d.startAngle + d.endAngle) / 2 < Math.PI * 1.5) {
return 17;
} else {
return 5;
}
})
.attr("text-anchor", function (d) {
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
return "beginning";
} else {
return "end";
}
}).text(function (d) {
return d.ind;
});
nameLabels.transition().duration(tweenDuration).attrTween("transform", textTween);
nameLabels.exit().remove();
updateBars(); //Notice the bar update gets called here and later, this actually works to create the neat into animation that only occurs on page load, easier than righting a custom delay function
}
// Interpolate the arcs in data space.
function pieTween(d, i) {
var s0;
var e0;
if (oldPieData[i]) {
s0 = oldPieData[i].startAngle;
e0 = oldPieData[i].endAngle;
} else if (!(oldPieData[i]) && oldPieData[i - 1]) {
s0 = oldPieData[i - 1].endAngle;
e0 = oldPieData[i - 1].endAngle;
} else if (!(oldPieData[i - 1]) && oldPieData.length > 0) {
s0 = oldPieData[oldPieData.length - 1].endAngle;
e0 = oldPieData[oldPieData.length - 1].endAngle;
} else {
s0 = 0;
e0 = 0;
}
var i = d3.interpolate({ startAngle: s0, endAngle: e0 }, { startAngle: d.startAngle, endAngle: d.endAngle });
return function (t) {
var b = i(t);
return arc(b);
};
}
function removePieTween(d, i) {
s0 = 2 * Math.PI;
e0 = 2 * Math.PI;
var i = d3.interpolate({ startAngle: d.startAngle, endAngle: d.endAngle }, { startAngle: s0, endAngle: e0 });
return function (t) {
var b = i(t);
return arc(b);
};
}
function textTween(d, i) {
var a;
if (oldPieData[i]) {
a = (oldPieData[i].startAngle + oldPieData[i].endAngle - Math.PI) / 2;
} else if (!(oldPieData[i]) && oldPieData[i - 1]) {
a = (oldPieData[i - 1].startAngle + oldPieData[i - 1].endAngle - Math.PI) / 2;
} else if (!(oldPieData[i - 1]) && oldPieData.length > 0) {
a = (oldPieData[oldPieData.length - 1].startAngle + oldPieData[oldPieData.length - 1].endAngle - Math.PI) / 2;
} else {
a = 0;
}
var b = (d.startAngle + d.endAngle - Math.PI) / 2;
var fn = d3.interpolateNumber(a, b);
return function (t) {
var val = fn(t);
return "translate(" + Math.cos(val) * (r + textOffset) + "," + Math.sin(val) * (r + textOffset) + ")";
};
}
update();//calls the above code to run,
});
//Start the bar graph code
var margin = { top: 10, right: 40, bottom: 20, left: 50 },
width = 450 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .2);
var y = d3.scale.linear()
.range([height, 0]);
var y2 = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
var y2Axis = d3.svg.axis()
.scale(y2)
.orient("right")
.ticks(4, "%");
var svg = d3.select("#graph").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var line = d3.svg.line()
.x(function (d) { return x2(d.periodyear); })
.y(function (d) { return y2(d.changeemp); });
function roundToOne(num) {//Ensures numbers always display the given number of digits after the decimal
return +(Math.round(num + "e+1") + "e-1");
};
updateBars();
function updateBars() {
d3.csv("emp_series_21.csv", type, function (error, data) {
if (error) throw error;
var I = document.getElementById('geo').selectedIndex
, select = document.getElementById('geo')[I].value
, data = data.filter(function (d) { return d.area == select })
, min = (d3.min(data, function (d) { return d.avgemp; }) - (.2 * d3.mean(data, function (d) { return d.avgemp; })))
, min2 = 1.2 * d3.min(data, function (d) { return d.changeemp; })
, max2 = 1.2 * d3.max(data, function (d) { return d.changeemp; })
if (min < 1) {//sets axis automatically, but uses zero if the min would be close to 0 anyway
var min = 0
};
if (min2 >= 0) {
var min2 = -.01
};
if (max2 <= 0) {
var max2 = .01
};
//Tufte would be proud of this, if he ever saw it. The economic data
//of y/y change in employment can take on negitive values, thus we need to
// ensure the axis always include negitive and positive values
x.domain(data.map(function (d) { return d.periodyear; }));
y2.domain([min2, max2]);
y.domain([min, ((d3.max(data, function (d) { return d.avgemp / 100; }) * 100) + (.11 * d3.mean(data, function (d) { return d.avgemp; })))]);
//Sets the domain based on the average, making the max .11 times the average greater than the max value, for asthetics only essentually
if (svg.selectAll(".x.axis")[0].length < 1) {//This stuff only occurs the first time the code is looped through, not subsiquent calls
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append('g')
.attr("class", "y axisL")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 4)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Annual Avg. Employment");
svg.append("g")
.attr("class", "y axisR")
.call(y2Axis)
.attr("transform", "translate(" + width + ",0)")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -11)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Y/Y Change");
svg.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function (d) { return x(d.periodyear); })
.attr("width", x.rangeBand())
.attr("y", function (d) { return y(10e-6); })
.attr("height", function (d) { return y(10e-6) })
.transition()
.duration(1050)
.attr("y", function (d) { return y(d.avgemp); })
.attr("height", function (d) { return height - y(d.avgemp); });
svg.selectAll(".chartLine")
.data(data)
.enter()
.append("rect")
.attr("class", "chartLine")
.attr("transform", "translate(" + 20 + ",0)")
.attr("width", x.rangeBand() - 40)
.attr("x", function (d) { return x(d.periodyear); })
.attr("y", function (d) { return d3.min(data, function (d) { return y2(d.changeemp); }) })
.attr("height", function (d) { return 1 })
.transition()
.duration(1050)
.attr("y", function (d) { return y2(d.changeemp); });
svg.selectAll(".chartLineText")
.data(data)
.enter()
.append('text')
.attr("class", "chartLineText")
.style("color", "#000")
.style("text-anchor", "middle")
.attr("transform", "translate(" + 30 + ",-2)")
.attr("x", function (d) { return x(d.periodyear); })
.attr("y", function (d) { return d3.min(data, function (d) { return y2(d.changeemp); }) })
.text(function (d) { return roundToOne(d.changeemp * 100) + "%" })
.transition()
.duration(1050)
.attr("y", function (d) { return y2(d.changeemp); });
svg.selectAll(".barText")
.data(data)
.enter()
.append("text")
.attr("class", "barText")
.attr("text-anchor", "middle")
.attr("transform", "translate(" + x.rangeBand() / 2 + ",-3)")
.attr("x", function (d) { return x(d.periodyear); })
.text(function (d) { return formatMoney(+d.avgemp) })
.attr("y", function (d) { return y(d.avgemp) - 10; });
} else {//This is what is called when the code is called to update by a change in the drop downs.
svg.selectAll(".y.axisL").transition().duration(1500).call(yAxis);
svg.selectAll(".y.axisR").transition().duration(1500).call(y2Axis);
svg.selectAll(".chartLineText").
data(data)
.transition()
.duration(1500)
.attr("y", function (d) { return y2(d.changeemp); })
.text(function (d) { return roundToOne(d.changeemp * 100) + "%" });
svg.selectAll(".chartLine")
.data(data)
.transition()
.duration(1500)
.attr("y", function (d) { return y2(d.changeemp); });
svg.selectAll(".bar")
.data(data)
.transition()
.duration(1500)
.delay(function (d) { return 100 + (d.periodyear - 2010) * 100 })
//Notice this delay function, it allows the eye to see all of the changes in the graph without needing to run the animation again,
//but it's suttle enough it's hard to notice, but it really makes a difference, your mind is fast but only holds so many things in
//short term memory, this delay helps avoid mental hurt, but also by limiting the years to 4 makes it much better
// Also the green bars draw in the focus while changing more than the small red lines, this is to mentally make it easier again. (I read to many theory books on this stuff, sorry those that are color blind)
.attr("y", function (d) { return y(d.avgemp); })
.attr("height", function (d) { return height - y(d.avgemp); });
svg.selectAll(".barText")
.data(data)
.transition()
.duration(500)
.attr("y", function (d) { return y(d.avgemp); })
.text(function (d) { return formatMoney(+d.avgemp) });
};
d3.selectAll(".bar")
.on("mouseover", function (d) {
d3.selectAll(".barText").transition()
.duration(500)
.delay(80)
//This makes the text lables mostly prevent overlap, it's not perfect, but solves more cases than it breaks, and the values are generally always visable
.attr("y", function (d) { if (Math.abs(y2(d.changeemp) - y(d.avgemp)) < 10) { if (y(d.avgemp) - 10 - (y2(d.changeemp)) >= 2) { return y(d.avgemp) - 10 } else { return y(d.avgemp) + 15 } } else { return y(d.avgemp) } })
.style("visibility", "visible");
})
.on("mouseout", function (d) {
d3.selectAll(".barText").transition()
.duration(500)
.delay(150)
.attr("y", function (d) { return y(d.avgemp) - 10; })
.style("visibility", "hidden");
})
});
function type(d) {
d.periodyear = +d.periodyear;
d.changeemp = +d.changeemp / 100;
d.avgemp = +d.avgemp;
return d;
}
};
</script>
</body>
</html>
area label ind emp wage gdp
00 New Mexico Oil & Gas 27869 1480
01 Bernalillo Oil & Gas 119 1092
03 Catron Oil & Gas
05 Chaves Oil & Gas 602 1244
06 Cibola Oil & Gas 446 1358
07 Colfax Oil & Gas 54 1402
09 Curry Oil & Gas
11 De Baca Oil & Gas
13 Do&ntilde;a Ana Oil & Gas 16 758
15 Eddy Oil & Gas 7275 1447
17 Grant Oil & Gas
19 Guadalupe Oil & Gas
21 Harding Oil & Gas
23 Hidalgo Oil & Gas
25 Lea Oil & Gas 8836 1442
27 Lincoln Oil & Gas
28 Los Alamos Oil & Gas
29 Luna Oil & Gas 15 524
31 Mckinley Oil & Gas 37 1881
33 Mora Oil & Gas
35 Otero Oil & Gas 74 709
37 Quay Oil & Gas
39 Rio Arriba Oil & Gas 52 533
41 Roosevelt Oil & Gas
43 Sandoval Oil & Gas 90 861
45 San Juan Oil & Gas 6723 1613
47 San Miguel Oil & Gas 10 456
49 Santa Fe Oil & Gas 102 1404
51 Sierra Oil & Gas
53 Socorro Oil & Gas
55 Taos Oil & Gas 228 1661
57 Torrance Oil & Gas
59 Union Oil & Gas
61 Valencia Oil & Gas 16 1111
00 New Mexico All Other Industries 771036 33494269861
01 Bernalillo All Other Industries 314491 13724432862
03 Catron All Other Industries 649 19265532
05 Chaves All Other Industries 20425 708393070
06 Cibola All Other Industries 7255 270491872
07 Colfax All Other Industries 5048 147949378
09 Curry All Other Industries 17024 589624583
11 De Baca All Other Industries 493 14836158
13 Do&ntilde;a Ana All Other Industries 70716 2497451079
15 Eddy All Other Industries 20553 1473527044
17 Grant All Other Industries 9352 364270696
19 Guadalupe All Other Industries 1323 36171285
21 Harding All Other Industries 201 7424078
23 Hidalgo All Other Industries 1586 59328586
25 Lea All Other Industries 24513 1780780154
27 Lincoln All Other Industries 6492 190115520
28 Los Alamos All Other Industries 15264 1164049550
29 Luna All Other Industries 7836 249167246
31 Mckinley All Other Industries 20127 656575592
33 Mora All Other Industries 690 20694037
35 Otero All Other Industries 17274 598825777
37 Quay All Other Industries 2652 76876297
39 Rio Arriba All Other Industries 9477 302626499
41 Roosevelt All Other Industries 6106 193908652
43 Sandoval All Other Industries 28782 1211010433
45 San Juan All Other Industries 43491 2253088206
47 San Miguel All Other Industries 8140 243709988
49 Santa Fe All Other Industries 60324 2582192168
51 Sierra All Other Industries 3352 92620127
53 Socorro All Other Industries 5167 182567934
55 Taos All Other Industries 10202 313311765
57 Torrance All Other Industries 3183 104263761
59 Union All Other Industries 1230 37985967
61 Valencia All Other Industries 13064 397956160
area label ind emp wage gdp
00 New Mexico Utilities 6219 416584266
01 Bernalillo Utilities 910 77837588
03 Catron Utilities 11 397367
05 Chaves Utilities 127 6135289
06 Cibola Utilities 246 13244250
07 Colfax Utilities 76 3980833
09 Curry Utilities 126 6433508
11 De Baca Utilities 28 781151
13 Do&ntilde;a Ana Utilities 725 38959907
15 Eddy Utilities 344 20874726
17 Grant Utilities 96 5005205
19 Guadalupe Utilities 22 591543
21 Harding Utilities
23 Hidalgo Utilities 12 886985
25 Lea Utilities 422 31014820
27 Lincoln Utilities 121 6164420
28 Los Alamos Utilities 93 5129947
29 Luna Utilities 67 4090930
31 Mckinley Utilities 205 15444464
33 Mora Utilities 26 1274836
35 Otero Utilities 157 7902158
37 Quay Utilities 58 2454598
39 Rio Arriba Utilities 131 7100111
41 Roosevelt Utilities 76 3832170
43 Sandoval Utilities 89 4232142
45 San Juan Utilities 1115 107861259
47 San Miguel Utilities 99 2919520
49 Santa Fe Utilities 254 15388724
51 Sierra Utilities 45 2103872
53 Socorro Utilities 92 3892224
55 Taos Utilities 151 6578513
57 Torrance Utilities 100 5624973
59 Union Utilities 31 1559375
61 Valencia Utilities 150 6189882
00 New Mexico Other Industries 792686 33077687075
01 Bernalillo Other Industries 313700 13646596366
03 Catron Other Industries 638 18868165
05 Chaves Other Industries 20900 702259025
06 Cibola Other Industries 7455 257248980
07 Colfax Other Industries 5026 143969947
09 Curry Other Industries 16898 583191075
11 De Baca Other Industries 465 14055007
13 Do&ntilde;a Ana Other Industries 70007 2458491930
15 Eddy Other Industries 27484 1452653765
17 Grant Other Industries 9256 359265491
19 Guadalupe Other Industries 1301 35579742
21 Harding Other Industries 189 6537093
23 Hidalgo Other Industries 1164 28313766
25 Lea Other Industries 33228 1774617176
27 Lincoln Other Industries 6399 184985573
28 Los Alamos Other Industries 15197 1159958620
29 Luna Other Industries 7646 233723306
31 Mckinley Other Industries 20138 655302637
33 Mora Other Industries 533 12791879
35 Otero Other Industries 17290 596371888
37 Quay Other Industries 2521 69776186
39 Rio Arriba Other Industries 9453 298794862
41 Roosevelt Other Industries 6017 189676510
43 Sandoval Other Industries 27757 1103150035
45 San Juan Other Industries 50115 2250170299
47 San Miguel Other Industries 7896 228321720
49 Santa Fe Other Industries 60381 2580089700
51 Sierra Other Industries 3260 88727903
53 Socorro Other Industries 5016 175989421
55 Taos Other Industries 10330 307688453
57 Torrance Other Industries 3152 102704386
59 Union Other Industries 1080 31796085
61 Valencia Other Industries 12930 391767389
@ndobie
Copy link
Author

ndobie commented Mar 4, 2016

A full interactive example can be found at the website I created these for: Bureau of Business and Economic Research at The University of New Mexico, many more sectors covered; easily scaleable to the size you need (why I created it in the first place).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment