Stacked Bar Chart with Time Series Data - csv

I am new to D3.js and am using D3 v4 for this. I have already seen https://bl.ocks.org/mbostock/3886208.
Here is the .csv file (test_data.csv):
date,col_1,col_2
11/1/2012,1977652,1802851
12/1/2012,1128739,948687
1/1/2013,1201944,1514667
2/1/2013,1863148,1834006
3/1/2013,1314851,1906060
4/1/2013,1283943,1978702
5/1/2013,1127964,1195606
6/1/2013,1773254,977214
7/1/2013,1929574,1127450
8/1/2013,1980411,1808161
9/1/2013,1405691,1182788
10/1/2013,1336790,937890
11/1/2013,1851053,1358400
12/1/2013,1472623,1214610
1/1/2014,1155116,1757052
2/1/2014,1571611,1935038
3/1/2014,1898348,1320348
4/1/2014,1444838,1934789
5/1/2014,1235087,950194
6/1/2014,1272040,1580656
7/1/2014,980781,1680164
8/1/2014,1391291,1115999
9/1/2014,1211125,1542148
10/1/2014,1020824,1782795
11/1/2014,1685081,926612
12/1/2014,1469254,1767071
1/1/2015,1168523,935897
2/1/2015,1602610,1450541
3/1/2015,1830278,1354876
4/1/2015,1275158,1412555
5/1/2015,1560961,1839718
6/1/2015,949948,1587130
7/1/2015,1413765,1494446
8/1/2015,1166141,1305105
9/1/2015,958975,1202219
10/1/2015,902696,1023987
11/1/2015,961441,1865628
12/1/2015,1363145,1954046
1/1/2016,1862878,1470741
2/1/2016,1723891,1042760
3/1/2016,1906747,1169012
4/1/2016,1963364,1927063
5/1/2016,1899735,1936915
6/1/2016,1300369,1430697
7/1/2016,1777108,1401210
8/1/2016,1597045,1566763
9/1/2016,1558287,1140057
10/1/2016,1965665,1953595
11/1/2016,1800438,937551
12/1/2016,1689152,1221895
1/1/2017,1607824,1963282
2/1/2017,1878431,1415658
3/1/2017,1730296,1947106
4/1/2017,1956756,1696780
5/1/2017,1746673,1662892
6/1/2017,989702,1537646
7/1/2017,1098812,1592064
8/1/2017,1861973,1892987
9/1/2017,1129596,1406514
10/1/2017,1528632,1725020
11/1/2017,925850,1795575
Here is page.html:
<!DOCTYPE html>
<!-- https://bl.ocks.org/mbostock/3886208 -->
<style>
rect.bar { fill: steelblue; }
</style>
<script src="https://d3js.org/d3.v4.js"></script>
<body>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 900 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// parse the date / time
var parseDate = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime()
.domain([new Date(2012, 11, 1), new Date(2017, 11, 31)])
.range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var z = d3.scaleOrdinal().range(["#F1F1F1", "#CE1126"])
// load .csv file
d3.csv("test_data.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data){
if (error) throw error;
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var keys = data.columns.slice(1);
data.sort(function(a, b) { return b.date - a.date; });
x.domain(data.map(function(d){ return d.date }));
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
console.log(d3.stack().keys(keys)(data));
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return console.log(x(d.data.date)); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());
});
</script>
</body>
My aim is to create a stacked bar chart by month based on the link above (so each tick corresponds to a month), of the values in col_1 and col_2.
Some errors I've received:
console.log(x(d.data.date)), for some reason, does not return the actual date.
The following error:
Uncaught TypeError: x.bandwidth is not a function
at page.html:65
at Object. (d3.v4.js:11453)
at Dispatch.call (d3.v4.js:792)
at XMLHttpRequest.respond (d3.v4.js:11378)
I think this error might have something to do with the fact that I'm using scaleTime, rather than scaleBand, but I'm not sure.

There are some issues with your dates, which need to be in working order to use a time scale.
Firstly, Your date format is 4/1/2015 but you are parsing it with d3.timeParse("%Y-%m-%d");
You will only get null values parsing with this, to see this try:
data.forEach(function(d) {
d.date = parseDate(d.date);
console.log(d.date); // null
});
As you use slashes rather than dashes, and you use month, day, year, try:
d3.timeParse("%m/%d/%Y");
Secondly, and related to dates is that you set an initial domain based on hard coded minimum and maximum dates, that works, but you overwrite it with this:
x.domain(data.map(function(d){ return d.date }));
A timescale domain takes in a minimum and a maximum for the domain. You are providing an array of every value, instead, use:
x.domain(d3.extent( data, function(d){ return d.date }) );
On to the graph and bar width, .bandwidth() isn't a method of a continuous scale (hence your error: "Uncaught TypeError: x.bandwidth is not a function"). In a continuous scale the bars are placed based on their value, there is no natural bar width (bars are not placed with regular spacing unless the increment between each data value is the same).
As you have a data point for each month (there are no holes in the data), and each data point represents an equal length in time, we can set bar width to be a fraction of the plot area width, where the width of the plot area is split equally between each data point:
(width-margin.right-margin.left)/data.length
Here's your graph with these changes:
And here's a working example.
Lastly, note that you need to return a value here:
.attr("x", function(d) { return console.log(x(d.data.date)); })
Log before the return statement if you want to debug:
.attr("x", function(d) { console.log(x(d.date.date)); return x(d.data.date); })

Related

Rollup SUM Values Not Working When Trying To Use In Bar Graph

I've imported data from a CSV file, managed to create a sum of "Score" column against each "Dimension" column, as well as get a distinct count of attributes under the "Dimension" column using the .map function:
d3.csv("TraitData.csv", function(d) {
return {
city: d.City,
continent: d.Continent,
country: d.Country,
dimension: d.Dimension,
identity: d.Identity,
score: +d.Score,
state: d.Subdivision,
trait: d.Trait,
index: d.Index,
longitude: d.Latitude,
latitude: d.Longitude
}
}).then(function(data) {
var varScore = d3.nest()
.key(function(d) { return d.dimension; })
.rollup(function(v) { return {
totalscore: d3.sum(v, function(d) { return d.score; }),
}; })
.entries(data);
var varDimensionGroup = d3.map(data, function(d) {return d.dimension;}).keys();
var varDimension = varDimensionGroup.length;
var svgWidth = 1250;
var svgHeight = 300;
var barPadding = 5;
var barWidth = (svgWidth/varDimension);
console.log(varScore);
console.log(varDimensionGroup);
console.log(varDimension);
console.log(svgWidth);
console.log(svgHeight);
console.log(barPadding);
console.log(barWidth);
var svg = d3.select("body").selectAll("#bar")
.append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
var barChart = svg.selectAll("rect")
.data(varDimensionGroup)
.enter()
.append("rect")
.attr("height", varScore)
.attr("width", barWidth - barPadding)
.attr("transform", function (d,i) {
var translate = [barWidth * i, 0];
return "translate("+ translate +")";
});
});
But I'm getting an error where the bar chart's height is concerned:
Note that it says "5 errors" because my full CSV dataset has 5 Dimensions.
Obviously the way I'm calculating the SUM of each, or the way I'm trying to utilize / reference it is wrong, but I'm not sure why. D3 is pretty new to me.
The csv file contains the following:
City,Continent,Country,Dimension,Identity,Score,Subdivision,Trait,Index,Latitude,Longitude
Wilmington,North America,United States,Pride,1270858,45,Delaware,Ego,1,"39,7932","-75,6181"
Wilmington,North America,United States,Humility,1270858,23,Delaware,Selflessness,2,"39,7932","-75,6181"
Wilmington,North America,United States,Humility,1270858,23,Delaware,Generosity,3,"39,7932","-75,6181"
Wilmington,North America,United States,Anger,1270858,48,Delaware,Impatience,4,"39,7932","-75,6181"
To summarize, what I'm trying to show is a bar chart, where the number of bars is based on the number of distinct "Dimensions" and the height of each bar is based on the sum of "Score" for each dimension.
I'm using D3.js v5.
use the following:
var barChart = svg.selectAll("rect")
.data(varScore)
.enter()
.append("rect")
.attr("height", function(d){return d.value;} )
.attr("width", barWidth - barPadding)
.attr("transform", function (d,i) {
var translate = [barWidth * i, 0];
return "translate("+ translate +")";

D3.js Heatmap: How to read from a nested json and create a Heatmap using D3

I have been trying for a couple of days now to read a JSON file with data and generate a heatmap from it. The JSON contains the frequency of git commits by day, for every week for 52 weeks.The data in the JSON (frequency each day) is nested hence I do not know how to extract the data and get the code to represent it. I am really new to do and I do not understand what I am missing or doing wrong. Would really appreciate if anyone can help me out. I read several links, but none of them deals with JSON and I am also having trouble understanding it. I would like to create 52 rows representing a week and 7 columns for each day of the week which I already have. The rectangles just dont reflect the frequency. I am not sure how to do that. A sample of the data I have and the code I have done so far is as such:
Code:
<script type='text/javascript' src='http://d3js.org/d3.v4.min.js'></script>
<script>
var url = "data/git-commit-frequency.json"
var color = ["#ffffd9","#c7e9b4","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]
d3.json(url, function (data) {
let gridSize = 50;
let width = (gridSize + 1) * 7;
let height = (gridSize + 1) * data.length;
// define range!!!
var x = d3.scaleLinear()
.domain([0, 7])
.range([0, width]);
//52 weeks
var y = d3.scaleLinear()
.domain([0, data.length])
.rangeRound([height, 0]);
let svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
// Generate rows for each json object
var rows = svg.selectAll('.row')
.data(data)
.enter()
.append('g')
.attr("transform", function(d, i){
return "translate(0," + y(i) + ")"
})
// Generate rects for the array of days per object
let box = rows.selectAll("rect")
.data(function(d){ return d.days })
.enter()
.append("rect")
.attr("x", function(d, i) { return x(i); })
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", 'black')
.attr("class", "bordered");
})
</script>
</body>
JSON:
[
{
"days": [
0,
0,
0,
0,
0,
1,
0
],
"total": 1,
"week": 1457827200
}
]
I have 52 of such JSON objects in the file.
Your code attaches values to the rects with this piece of code, and specifically the .data(function(d){ return d.days })
let box = rows.selectAll("rect")
.data(function(d){ return d.days })
.enter()
.append("rect")
.attr("x", function(d, i) { return x(i); })
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", 'black')
.attr("class", "bordered");
})
Each appended rect will have one of the values from the days array (eg 0, or 1).
You can inspect this in the browser console (right click on the rect, select Inspect and go to Properties > Rect > data and you will see what values have been appended to that element.
From there, you can typically access the value using an anonymous function within setting the style or attr of your rect. For example, to set colour:
.style("fill", function(d) { return d === 0 ? "grey" : "black"; })
which sets rects with 0 value to be grey, and anything else black. Although you as you get more complex mappings, using d3-scale becomes valuable.

d3 multiple line graphs

I have two problems I am struggling to understand or solve in my following d3 code (showing simplest version of the problem). Background is that I am trying to plot lines from a csv file (entire contents pasted at the end and full code that runs also below). The first 2 lines (to illustrate the format of csv) are:
date,cust,prod,units
2012-04-01,team1,A,34
Basically there are customers (team1, team2, team3) that purchase units of products (A, B,C,D). I am trying to draw line graphs filtered by product (A, B, C, D). This works individually. However since the units and date ranges vary for each product (A, B, C, D) I am first trying to fix my range to be maximum date range and max units sold. When I do this, the axis look correct but the graph does not get drawn. If I use just the filtered data the graphs works fine. What am I missing? I don't see any JS errors in the console. My next goal is to add and remove the graphs (via checkboxes) without redrawing anything, whats the easiest way to achieve that?
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
#line1 {
fill: none;
stroke: steelblue;
stroke-width: 1px;
}
</style>
<div id="customer">
<input class="custCB" type="checkbox" id="D_CB" name="cust" value="D" enabled onclick="showGraph('D')"> D<br>
<input class="custCB" type="checkbox" id="C_cb" name="cust" value="C" enabled onclick="showGraph('C')"> C<br>
<input class="custCB" type="checkbox" id="B_cb" name="cust" value="B" enabled onclick="showGraph('B')"> B<br>
<input class="custCB" type="checkbox" id="A_cb" name="cust" value="A" enabled onclick="showGraph('A')"> A<br>
</div>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
showGraph("D");
function showGraph(prod_name) {
var mydomain = 5000;
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%Y-%m-%d");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().domain([mydomain, 0]).range([height, 0]);
//console.log(y);
// define the 1st line
var valueline = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.units);
});
d3.select("svg").remove();
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("id", "parentgroup")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.csv("test2.csv", function (error, tdata) {
if (error) throw error;
data = tdata.filter(e=> e.prod===prod_name);
//_data = data.filter(e=> e.cust==="team2");
//console.log("__data_", _data);
// format the data
console.log("data:", data);
data.forEach(function (d) {
d.date = parseTime(d.date);
d.units = parseInt(d.units);
});
tdata.forEach(function (d) {
d.date = parseTime(d.date);
d.units = parseInt(d.units);
});
x.domain(d3.extent(tdata, function (d) {
console.log(d.date);
return d.date;
}));
//console.log("data", data);
var m = d3.max(tdata, function (d) {
console.log(d.units);
var m = parseInt(d.units);
return m;
});
console.log("Max:", m);
y.domain([0, m]);
console.log("tdata:", tdata);
//console.log("data:", data);
svg.append("path")
.data([data])
.attr("id", "line1")
.attr("d", valueline);
//console.log("DATA", data);
svg.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("cx", function (d) {
return x(d.date);
})
.attr("cy", function (d) {
return y(d.units);
})
.attr("r", 4)
.on("mouseover", function (d) {
console.log(d.units)
});
svg.append("g")
.attr("id", "xaxis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.attr("id", "yaxis")
.call(d3.axisLeft(y));
});
}
</script>
</body>
test2.csv
date,cust,prod,units
2012-04-01,team1,A,34
2012-04-02,team1,B,45
2012-04-03,team2,D,67
2012-04-04,team1,A,78
2012-04-05,team3,C,89
2012-04-06,team2,D,99
2012-04-07,team2,A,101
2012-04-08,team3,A,122
2012-04-09,team1,C,134
2012-04-10,team1,C,160
2012-04-11,team2,C,180
2012-04-12,team2,D,210
2012-04-13,team3,D,223
2012-04-14,team1,D,229
2012-04-15,team1,D,241
2012-04-16,team2,D,258
2012-04-17,team2,C,350
2012-04-18,team3,D,305
2012-04-19,team3,B,335
2012-04-20,team2,B,375
2012-04-21,team3,D,345
2012-04-22,team1,A,534
2012-04-23,team1,C,578
2012-04-24,team2,A,590
2012-04-25,team1,B,601
2012-04-26,team3,B,387
2012-04-27,team2,C,613
2012-04-28,team2,D,645
2012-04-29,team3,D,410
2012-04-30,team1,A,612
2012-05-01,team2,A,670
2012-05-02,team3,A,657
2012-05-03,team1,A,690
2012-05-04,team3,A,709
2012-05-05,team2,C,690
2012-05-06,team3,B,740
2012-05-07,team1,A,1000
This snippet is problematic:
data.forEach(function (d) {
d.date = parseTime(d.date);
d.units = parseInt(d.units);
});
tdata.forEach(function (d) {
d.date = parseTime(d.date);
d.units = parseInt(d.units);
});
tdata and data are arrays holding references to the same objects. So the second forEach is then acting on the same objects and the parseTime fails. Just do:
tdata.forEach(function(d) {
d.date = parseTime(d.date);
d.units = parseInt(d.units);
});
var data = tdata.filter(e => e.prod === prod_name);
Here's your code all cleaned up.

max and min values from json in d3

I'm following a tutorial of d3 that uses tsv rather than json.
I'm more familiar with json so I wanted to apply my own json object to the example.
I found this and tried to apply it but i'm still getting errors:
How to use d3.min and d3.max within a d3.json command
Here is my code. I am getting the json object back with my first log. And the correct length with my second log.
Yet I get this error when trying to use d.value in d3.max()
Error: Invalid value for attribute width="NaN"
d3.json("sampledata.json", function(error, data) {
var maxx = d3.entries(data)
.sort(function(a,b){return d3.descending(a.value, b.value);})
console.log(maxx)
console.log(data.length);
mydata = data;
x.domain([0, d3.max(data, function(d) { return d.value; })])
chart.attr("height", barHeight * mydata.length);
var bar = chart.selectAll("g")
.data(mydata)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
// WHERE MY ERROR IS because there is no value for x
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
I think what you need to do, since x is a scale of some kind, is the following:
bar.append("rect")
.attr("width", function(d) {
return x(d.value);
})
.attr("height", barHeight - 1);
Otherwise it is just going to pass d into x, which isn't going to help you.

How can I perform this d3.js magic (Grouped Bar Chart) with JSON rather than CSV?

I am attempting to follow this example: http://bl.ocks.org/3887051
However, instead of a CSV, I have a JSON object.
Now, I saw that I could convert my JSON into a CSV by following the instructions here: How to convert JSON to CSV format and store in a variable
That feels like a hack though.
Here's my JSON:
[{"YEAR":2012,"MONTH":1,"MMM":"JAN","Total_Flights":30,"Completed":21,"Canceled":7,"Aborted":2},
{"YEAR":2012,"MONTH":2,"MMM":"FEB","Total_Flights":54,"Completed":28,"Canceled":20,"Aborted":6},
{"YEAR":2012,"MONTH":3,"MMM":"MAR","Total_Flights":39,"Completed":25,"Canceled":12,"Aborted":2},
{"YEAR":2012,"MONTH":4,"MMM":"APR","Total_Flights":27,"Completed":21,"Canceled":6,"Aborted":0},
{"YEAR":2012,"MONTH":5,"MMM":"MAY","Total_Flights":35,"Completed":21,"Canceled":12,"Aborted":2},
{"YEAR":2012,"MONTH":6,"MMM":"JUN","Total_Flights":15,"Completed":10,"Canceled":4,"Aborted":1},
{"YEAR":2012,"MONTH":7,"MMM":"JUL","Total_Flights":42,"Completed":18,"Canceled":21,"Aborted":3},
{"YEAR":2012,"MONTH":8,"MMM":"AUG","Total_Flights":43,"Completed":29,"Canceled":8,"Aborted":6},
{"YEAR":2012,"MONTH":9,"MMM":"SEP","Total_Flights":28,"Completed":20,"Canceled":8,"Aborted":0},
{"YEAR":2012,"MONTH":10,"MMM":"OCT","Total_Flights":43,"Completed":24,"Canceled":18,"Aborted":1},
{"YEAR":2012,"MONTH":11,"MMM":"NOV","Total_Flights":35,"Completed":18,"Canceled":17,"Aborted":0},
{"YEAR":2012,"MONTH":12,"MMM":"DEC","Total_Flights":45,"Completed":9,"Canceled":32,"Aborted":4},
{"YEAR":2013,"MONTH":1,"MMM":"JAN","Total_Flights":49,"Completed":4,"Canceled":43,"Aborted":2}]
My game plan is to have the chart display four bars for each month: Total, Completed, Canceled and Aborted.
How would I transform this example code to deal with my JSON?
I'll be swimming through the d3.js tutorials here: https://github.com/mbostock/d3/wiki/Tutorials until I figure it out or someone wise shows me the ropes.
I'll periodically update this post with my progress.
UPDATE #1: Anyone coming here should check out these godsend tutorials: http://alignedleft.com/tutorials/d3/
I'm still working on it. Such a powerful library. I'll report back again within a few hours.
After a few days of diving all in D3's documentation and the great tutorials I linked to earlier, I finally created my first graph.
// Make a JSON object out of the data.d string receieved.
json_data = jQuery.parseJSON(data.d)
// width = 960 - 40 - 20 = 900
// height = 500 - 20 - 30 = 450
var margin = { top: 20, right: 20, bottom: 30, left: 40 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Information on ordinal scales can be found at: https://github.com/mbostock/d3/wiki/Ordinal-Scales
// An ordinal scale that sets the output range from the specified continuous interval (that is, [0, width]).
// The array interval contains two elements representing the min and max numeric values.
// This interval is subdivided into n evenly-spaced bands, where n is the number of (unique) values in the domain.
// The bands may be offset from the edge of the interval and other bands according to the specifided padding,
// which defaults to zero.
// The padding is typically in the range [0,1] (0.1 in this example) and corrseponds to the amount of space
// in the range interval to allocate to padding.
// A value of 0.5 means that the band width will be equal to the padding width.
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
// Constructs a new ordinal scale with an empty domain and an empty range.
// The ordinal scale is invalid (always returning undefined) until an output range is specified).
var x1 = d3.scale.ordinal();
// Information on linear scales can be found at: https://github.com/mbostock/d3/wiki/Quantitative-Scales
// Quantitative scales have a continuous domain, such as the set of real numbers, or dates.
// Linear scales are a type of quantitative scale.
// Linear scales are the most common scale, and a good default choice to map a continuous input domain to a
// continous output range.
// The mapping is linear in that the output range value y can be expressed as a linear function of the
// input domain value x: y = mx + b.
// The input domain is typically a dimension of the data that you want to visualize, such as the height of
// students (measured in meters) in a sample population.
// The output range is typically a dimension of the desired output visualization, such as the height of bars
// (measured in pixels) in a histogram.
// This will set up our y height scale.
var y = d3.scale.linear()
.range([height, 0]);
// Colors of the graph.
//
// First : Total flights #097054 (green)
// Second : Completed flights #6599FF (blue)
// Third : Cancelled flights #FFDE00 (yellow)
// Fourth : Aborted flights #FF9900 (orange)
var color = d3.scale.ordinal()
.range(["#097054", "#6599FF", "#FFDE00", "#FF9900"]);
// Set up the xAxis to use our x0 scale and be oriented on the bottom.
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
// We don't worry about tickFormat here, as the ticks will be determined by the data.
// Set up the yAxis to use our y scale and be oriented on the left.
// Additionally, set the tick format to display appropriate labels on the axis (taking out for now).
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// .tickFormat(d3.format(".2s"));
// Set up the svg canvas with the width and height we calculated earlier.
var svg = d3.select("body").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 + ")");
// Move it to the right margin.left pixels, and move it down margin.top pixels
// Our JSON looks like:
// [{ "YEAR": 2012, "MONTH": 1, "MMM": "JAN", "Total": 36, "Completed": 21, "Canceled": 10, "Aborted": 5 },
// { "YEAR": 2012, "MONTH": 2, "MMM": "FEB", "Total": 60, "Completed": 30, "Canceled": 21, "Aborted": 9 }]
// data = d3.nest()
// .key(function (d) { return d.MMM + " " + d.YEAR; })
// .entries(json_data)
data = json_data
// seriesNames = "Total", "Completed", "Canceled" and "Aborted" See, we're filtering out "YEAR", "MONTH" and "MMM"
var seriesNames = d3.keys(data[0]).filter(function (key) { return (key !== "YEAR") && (key !== "MONTH") && (key !== "MMM"); });
// alert(JSON.stringify(seriesNames));
// alert(seriesNames);
data.forEach(function (d) {
d.Flights = seriesNames.map(function (name) { return { name: name, value: +d[name] }; });
//alert("hi --- " + JSON.stringify(d.Flights));
});
//alert(JSON.stringify(data));
//x0.domain(data.map(function (d) { return d.State; }));
// Change State to be MMM, YEAR (for example: "Jan 2012") Could change this to Jan '12
x0.domain(data.map(function (d) { return d.MMM + " " + d.YEAR; }));
//alert(JSON.stringify(data.map(function (d) { return d.MMM + " " + d.YEAR; })));
// //x1.domain(seriesNames).rangeRoundBands([0, x0.rangeBand()]);
x1.domain(seriesNames).rangeRoundBands([0, x0.rangeBand()]);
// //y.domain([0, d3.max(data, function (d) { return d3.max(d.ages, function (d) { return d.value; }); })]);
// // Make the y domain go from 0 up to the max of d.Total (Total flights)
// y.domain([0, d3.max(data, function (d) { return d3.max(d.Total); })]);
y.domain([0, (10 + d3.max(data, function (d) { return d3.max(d.Flights, function (d) { return d.value; }); }))]);
// The axis business
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("# of Flights");
// From this point to...
//var state = svg.selectAll(".state")
// .data(data)
//.enter().append("g")
// .attr("class", "g")
// .attr("transform", function (d) { return "translate(" + x0(d.State) + ",0)"; });
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function (d) { return "translate(" + x0(d.MMM + " " + d.YEAR) + ",0)"; });
//alert(JSON.stringify(d.Flights[0]));
state.selectAll("rect")
.data(function (d) { return d.Flights; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function (d) { return x1(d.name); })
.attr("y", function (d) { return y(d.value); })
.attr("height", function (d) { return height - y(d.value); })
.style("fill", function (d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(seriesNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) { return d; })
.on("click", function (d) {
alert(d);
//state.selectAll("rect")
//.update()
// .exit().transition()
// .attr("height", 0)
// .remove();
//state.selectAll("rect")
//.update()
//state.selectAll("rect").exit().transition().attr("height", 0).remove();
});
After this graph, it was very easy to make the multiple donut graphs as well as the stacked chart.