I have a real data set (https://data.gov.uk/dataset/road-traffic-accidents - 2009) which im trying to pull information from, it has roughly 2500 items, as a sample -
I'm using d3 to create a stacked bar chart which displays the categories on the x axis - "slight, serious, fatal" and the frequency of the accidents for each category on the y axis. the actual bars themselves will be split into how many of these accidents were female and male.
currently though, I'm trying to figure out how to best pull the information from the csv. Having had a look at a few examples of various d3 stacked charts, the data seems to be arranged like such (example from - http://www.adeveloperdiary.com/d3-js/create-stacked-bar-chart-using-d3-js/) -
var data=[
{month:'Jan', A:20, B: 5, C: 10},
{month:'Feb', A:25, B: 10, C: 20}
]
var xData = ["A", "B", "C"];
so using that example as a basis, i'm guessing maybe the best way format the data from this particular csv would be ? -
var data = [
{'Casualty Severity':'Slight', 'Male': 1567, 'Female': 1200 },
{'Casualty Severity':'Serious', 'Male': 100, 'Female': 120 },
{'Casualty Severity':'Fatal', 'Male': 15, 'Female': 5 }
]
unfortunately I am not very good at formatting the data, but roughly know how to pull using d3.nest
d3.csv('/road_accidents/2009.csv', function(data) {
var severity = d3.nest()
.key(function(d) {
return d['Casualty Severity'];
})
.entries(data);
console.log(severity)
})
which provides this out put in the console -
extended looking like this -
So i suppose, the question is what is the best way to format the data, using a real data set for use in a stacked bar chart, and would this be the correct way of doing it?
any help would be hugely appreciated!
This is one way to organize your data which would make it easy to use when building your stacked bar chart. It could be simplified further if you don't want it to be nested so much.
var url = "https://aql.datapress.com/leeds/dataset/road-traffic-accidents/2016-05-19T15:29:13/2009.csv";
d3.csv(url, function(data) {
var severityBySex = d3.nest()
.key(function(d) { return d['Casualty Severity'];})
.key(function(d) { return d['Sex of Casualty']; })
.entries(data);
for (var i = 0; i < severityBySex.length; i++){
console.log(severityBySex[i].key + " - " + "MALE: " +
severityBySex[i].values[0].values.length + " - "
+ "FEMALE: " + severityBySex[i].values[1].values.length);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I took example source from this link.
My data looks like this:
var data = [{ known_technologies : ["prepaid billing", " postpaid billing"],company : "A",YearsOfExp:0},
{ known_technologies : ["prepaid billing", " postpaid billing"],company:"B",YearsOfExp:1}];
My dimensions:
var ndx = crossfilter(data);
var yearDim2 = ndx.dimension(function(d)
{
return [d.known_technologies,d.company];
});
var spendPerYear2 = yearDim2.group();
I got something like below:
But I wanted in below way:
jsfiddle here
How do I make the dimensions. Any suggestions? Thanks in advance.
If you want to render two different sunbursts you need two different elements to grab on your html page. You can control the inner and out rings by changing the order of the dimensions in the array you are returning in the year2 dimension. Create appropriate groups, select both items from the DOM and create your charts as in the jsfiddle. Don't forget to call dc.renderAll() so you actually create the charts.
const ndx = crossfilter(data);
const yearDimX = ndx.dimension(function(d){
return [d.company, d.known_technologies];
});
const yearDimY = ndx.dimension(function(d){
return [d.known_technologies,d.company];
});
const spendPerYearX = yearDimX.group();
const spendPerYearY = yearDimY.group();
The data:
A 'premiums.tsv' file, with two columns, 'BiddingDate' and 'CategoryA'
What I'm trying to do:
Using d3, display in a table or divs just the latest BiddingDate and the CategoryA value that corresponds to that latest BiddingDate.
I've got a chart on the page, that works fine. I can also get a table of all the values. That works fine too. But I just can't figure out how to isolate the data corresponding to the latest date value and then display it. Would really appreciate any help. Thanks!
I solved it by using data.map to map the data into a new array, then simply calling the last item in the array.
I only ever have 241 data points, but for datasets of variable size, you can just use .length to figure out the latest data point.
var allDates = [];
allDates = data.map(function(d) {return d.BiddingMonth;});
var latestDate = allDates[240];
var allCatA = [];
allCatA = data.map(function(d) {return d.CategoryAPremium;});
var latestCatA = allCatA[240];
Then all I had to do was to print latestDate and latestCatA wherever I wanted.
d3.select("#lateDate").text("$ " + latestDate);
d3.select("#lateA").text("$ " + latestCatA);
Is it possible to use d3.js when opening new windows? For example, I am trying:
new_window = window.open("userpage.html");
new_window.document.write("<html><body>");
new_window.document.write("<table id=\"usertable\">");
new_window.document.write("</table>");
new_window.document.write("</body></html>");
table = d3.select("#usertable");
console.log(table);
var thead = table.append("thead");
var tbody = table.append("tbody");
var columns = ["dataset"];
thead.append("tr")
.selectAll("th")
.data(columns)
.enter()
.append("th")
.text(function(column) { console.log(column); return column; });
It doesn't work and the ouput of the first console.log is
[
Array[1]
0: null
length: 1
parentNode: HTMLHtmlElement
__proto__: Array[0]
]
I think 0: null is not good.
There are a few issues here:
I think you're opening the new window incorrectly - generally, you either open a URL with content, or you use "" as the URL and write your content into a blank window. Opening a URL like "usertable.html" and then writing <html><body> doesn't make sense. Finally, even with a blank window, you don't need to write <html><body> - the browser will generally provide these nodes by default.
Using d3.select is going to look, by default, in the current document. In order to access the body of the newly opened window, you'll need to pass in new_window.document - in fact, you'll need to pass in new_window.document.body, since you can't append anything to document without a HIERARCHY_REQUEST_ERROR.
I also don't think it's a good idea to mix D3 with document.write as you're doing here. D3 selects nodes in the DOM, and the way you have the code now, I don't think your table is actually a well-formed node until after you've tried to select it. D3 is perfectly good at inserting new DOM nodes - use it instead.
Putting all this together yields something like this:
var newWindow = window.open('');
var newWindowRoot = d3.select(newWindow.document.body);
// now do some writing with D3
var data = [
{ foo: "Foo 1", bar: "Bar 1" },
{ foo: "Foo 2", bar: "Bar 2" }
];
var table = newWindowRoot.append('table');
var rows = table.selectAll('tr')
.data(data);
rows.enter().append('tr');
var cells = rows.selectAll('td')
.data(function(d) { return d3.entries(d); });
cells.enter().append('td');
cells.text(function(d) { return d.value; });
Working example: http://jsfiddle.net/nrabinowitz/gQf7J/
I have a jqPlot chart that I want to add links on and I believe I figured out a way to do it using an array such as [[[1,2,"http://google.com"]],[[2,3,"http://yahoo.com]]] however, when I try to load this via XML, jQuery, and Ajax it doesn't quite work.
I believe that the problem lies within the .each clauses found in this code:
function getBars(xml)
{
var categoryid = 1;
var bars = [];
$(xml).find("category").each(
function()
{
bars.push(loadBars(categoryid,$(this)));
categoryid++;
});
return bars;
}
function loadBars(categoryid,xml)
{
var bar = [];
var bars = [];
$(xml).find("bar").each(function()
{
bar.push(parseInt(categoryid));
bar.push(parseInt($(this).attr("size")));
bar.push($(this).attr("link"));
bars.push(bar);
});
$("#debug").append("\nBAR:")
debug2dArray(bars);
return bars;
}
The XML looks like:
<?xml version="1.0"?>
<chart>
<category>
<bar size="20" link="http://google.com"/>
</category>
<category>
<bar size="70" link="http://yahoo.com" />
</category>
</chart>
Here is a jsFiddle
Update
After updating the variables to be non-global, the chart now displays right, but two of the same values are still being added to the array. Code has been updated to reflect changes.
I haven't digested your whole code yet, but one really fatal pitfall you're doing is using variables in your functions that haven't been declared with var (I'm particularly looking at how you've used your bar variable on both functions).
When you use a variable without declaring it with var like you're doing here, you're bringing the variable to a global visibility. That means that that variable is the same variable used (most) everywhere in your code. The same bar in the first function is the same bar in the second.
When your two functions start, the first thing it does is clear the bar variable (i.e. bar = [];). Since they're sharing bar references, calling one function effectively nullifies what the other did.
Is this your intention? If not (or even so), you should declare your variable with var:
var categoryId = 1,
bar = [];
In addition to the lack of var, you are returning variables at the end of the each iterators, instead of the end of the function. Here's a working fiddle: http://jsfiddle.net/fwRSH/1/
function loadBars(categoryid, xml) {
var bar = [];
var bars = [];
$(xml).find("bar").each(function() {
bar.push(parseInt(categoryid, 10));
bar.push(parseInt($(this).attr("size"), 10));
bar.push($(this).attr("link"));
bars.push(bar);
//$("#debug").append("\nBAR:"); //not defined in fiddle, commented out
//debug2dArray(bars); //not defined in fiddle, commented out
});
return bars; //moved from end of "each" iterator to here.
}
function getBars(xml) {
var categoryid = 1;
var bars = [];
$(xml).find("category").each(function() {
bars.push(loadBars(categoryid, $(this)));
categoryid++;
});
return bars;
}
$(document).ready(function() {
var bars = [];
$("div#barchart").css("background-color", "#F00");
$("div#barchart").css("height", "200px");
$("div#barhcart").css("width", "400px");
//moved for debugging
bars = getBars($('div#xmlDI'));
/* returns:
* [
* [
* [1, 20, "http://google.com"]
* ],
* [
* [2, 70, "http://yahoo.com"]
* ]
* ]
*/
$.jqplot("barchart", bars, {
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
fillToZero: true
}
},
axes: {
// Use a category axis on the x axis and use our custom ticks.
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
ticks: ['one', 'two'],
autoscale: true
},
yaxis: {
autoscale: true
}
}
});
});
None of your variables are declared using var, particularly the bars array. This causes them to be implicitly global, and you overwrite the variable every time you call loadBars.
I am not sure how you want your graph to look like. Because the data you provide to the graph, in general terms, is 'correctly' displayed. If you would write it in the following way:
[
[[1, 30, "http://google.com"], [2,0,""]],
[[1,0,""],[2, 40, "http://yahoo.com"]]
]
...it would give exactly the same results, the library just assumes that the data which is not provided for a particular series is 0 and this is how it is treated as visible here.
Since you do not like it this way my guess is that you made a formatting error in your data variable, as we can see here the 'gap' is gone.
Therefore, I think that the below is the format you are after:
[[
[1, 30, "http://google.com"],
[2, 40, "http://yahoo.com"]
]]
Additionally, as it goes to clicking on a bar of a bar chart you could find useful the answer to the problem. There you could see how to capture the click and how to open a URL. You would just need to slightly adopt it to your need as I used a global array of URLs.
Code to parse the XML:
var bars = [], cat = 0;
$.ajax({
type: 'GET',
url: 'plotlinks.xml',
dataType: "xml",
cache: true,
success: function(data, textStatus, jqXHR) {
$(data).find("category").each( function() {
var barSet = [cat];
$(this).find("bar").each(function() {
var $elt = $(this);
barSet.push([$elt.attr('size'),$elt.attr('link')]);
});
cat++;
bars.push(barSet);
});
// bars is an array; each element is an array.
// The first element in the inner array is the
// category "index" (0,1,2,...). All other
// elements represent a link for that category.
// Those elements are arrays of [size,url].
alert($.stringifyJSON(bars));
}
});
Resulting json:
[[0,
["20","http://google.com"]
],
[1,
["70","http://yahoo.com"]
]
]