How to use CSV data in d3 - d3.js

What I want to do
My CSV looks like this
,,Name,First names,s,r,Nat,born,starting point,starting date,arrival date,days,km,Assist,Support,Style,note,arrival date 2
1,1,KAGGE,Erling,,,Nor,1/15/1963,Berkner Island,11/18/1992,1/7/1993,50,appr. 1300,n,n,solo,first solo unassisted,
2,2,ARNESEN,Liv,f,,Nor,6/1/1953,Hercules Inlet,11/4/1994,12/24/1994,50,1130,n,n,solo,first woman unassisted,
3,3,HAUGE,Odd Harald,,,Nor,1956,Berkner Island,11/4/1994,12/27/1994,54,appr. 1300,n,n,,,
How should I import my CSV so that I will be able to access data such as
.text(function(d) {
return d['starting point'];
});
What I've tried
I have tried nesting the data
d3.csv("data.csv", function(csv_data) {
var data = d3.nest()
.key(function(d) {
return d.Name;
})
.entries(csv_data)
console.log(data);
which in my console looks something like this:
0: Object
key: " DICKINSON"
values: Array[2]
0: Object
Assist: "n"
First names: "Conrad"
Name: " DICKINSON"
Nat: "UK"
starting point: "Hercules Inlet"
1:Object
2:Object
....
However I am only able to return d.key
.text(function(d) {
return d.key;
});
I have asked this question before with code examples but I think my question was too confusing. If anyone could help me get to my data I would be really grateful!

Don't nest it.
d3.csv("data.csv", function(csv_data) {
select("body")
.append("div")
.selectAll('p')
.data(csv_data)
.enter()
.append('p')
.text(function(d) { d.Name + ', ' + d.location...})
});

Related

dc.js geoChoroplethChart doesn't display legend

I'm trying to plot a dc choropleth , but somehow the legend is not showing up.
Here is the sample fiddle :
http://jsfiddle.net/susram/9VJHe/56/
usChart
.width(1200)
.height(500)
.dimension(state_dim)
.group(latest_mean_sqft_per_state)
//.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
.colors(d3.scale.quantize().range(["#fff7fb","#ece2f0","#d0d1e6","#a6bddb","#67a9cf","#3690c0","#02818a","#016c59","#014636"]))
//.colors(d3.scale.quantize().range(d3.schemeBlues()(9)))
.colorDomain([0, 500])
//.colorAccessor(function (d) { /*console.log(d);*/ return d? usChart.colors(d.avg_psft) : '#ccc'; })
.colorAccessor(function (d) { /*console.log(d);*/ return d.avg_psft; })
.overlayGeoJson(statesJson.features, "state", function (d) {
return d.properties.name;
})
.valueAccessor(function(kv) {
console.log(kv);
return kv.value;
})
.title(function (d) {
return "State: " + d.key + "\nAverage Price per SqFt: " + numberFormat(d.value.avg_psft ? d.value.avg_psft : 0) + "M";
})
.legend(dc.legend().x(1300).y(80).itemHeight(13).gap(5));
Why is the legend showing up as 0x0 ?
I've been trying to get the legend to work with geoChoroplethCharts as well and unfortunately legend support appears to not have been implemented yet in dc. There are a few functions (legendables, legendHighlight, legendReset, legendToggle, ect...) that were defined in the dc base-mixin and would need to be extended before legend support would work.
For an example take a look at the source for pieChart:
https://github.com/dc-js/dc.js/blob/develop/src/pie-chart.js
Versus the soruce for geoChoroplethChart:
https://github.com/dc-js/dc.js/blob/develop/src/geo-choropleth-chart.js
You'll notice at the bottom of the pieChart source that the related legend functions were extended. I belive something similar would need to be done for the geoChoroplethChart source code.
EDIT:
I worked off your jsfiddle and was able to get a bare bones label to display on the geoChoroplethChart: http://jsfiddle.net/Lx3x929v/2/
usChart.legendables = function () {
return usChart.group().all().map(function (d, i) {
var legendable = {name: d.key, data: d.value, others: d.others,
chart: usChart};
legendable.color = usChart.colorCalculator()(d.value);
return legendable;
});
};
Here is my modification —for a continuous map— from #MarcTifrea 's solution and comment.
chart.legendables = function () {
var domain = chart.colorDomain();
return domain.map(function (d, i) {
var legendable = {name: parseFloat((Math.round(domain[i] * 100000) /100000).toPrecision(2)) , chart: chart};
if (i==1) legendable.name += ' unit'; // add the unit only in second(last) legend item
legendable.color = chart.colorCalculator()(domain[i]);
return legendable;
});
};
chart.legend(
dc.legend()
.x(width/4)
.y(height*4/5)
.itemHeight(height/30)
// .itemWidth(width/25)
.gap(5)
// .horizontal(1)
// .autoItemWidth(1)
);

NaN-Error at d3js error bars

Here is the important part of my code. For some reasons, there is a problem with the "errorBars2". It always returns this error:
Invalid value for <path> attribute d="M40.5,NaNL40.5,NaNZ"
I spent now hours on searching for the mistake but I can't find it! Could somebody explain me what's the problem with the "errorBars2"?
In errorBarArea2 you have written for
.y0(function (d) {
return y0(+d.top_after + +d.moe3);
})
but in the JSON structure it is not available change it to +d.moe3_after
and you have written
.y1(function (d) {
return y0(+d.low_after - +d.moe4);
})
but in the JSON structure it is not available change it to +d.moe4_after
So the final code is
var errorBarArea2 = d3.svg.area()
.x(function (d) {
return x3(d.name) +x3.rangeBand()/2;
})
.y0(function (d) {
return y0(+d.top_after + +d.moe3_after);
})
.y1(function (d) {
return y0(+d.low_after - +d.moe4_after);
})
Tell me if this works.
var errorBarArea2 = d3.svg.area()
.x(function (d) {
return x3(d.name) +x3.rangeBand()/2;
})
.y0(function (d) {
return y2(d.top_after + +d.moe3);
})
.y1(function (d) {
return y2(d.low_after - +d.moe4);
})

D3 sort() with CSV data

I am trying all kinds of ways to make .sort() work on my csv dataset. No luck.
I'd just like to sort my data by a "value" column.
This is the function I'm running inside my d3.csv api call and before I select the dom and append my divs:
dataset = dataset.sort(function (a,b) {return d3.ascending(a.value, b.value); });
Before I get to the .sort, I clean the data:
dataset.forEach(function(d) {
d.funded_month = parseDate(d.funded_month);
d.value = +d.value;
});
};
Everything seems in order. When I console.log(d3.ascending(a.value, b.value)), I get the right outputs:
-1 d32.html:138
1 d32.html:138
-1 d32.html:138
1 d32.html:138
etc..
Yet the bars data doesn't sort.
It is not clear from the provided code but I will hazard a guess you are not handling async nature of d3.csv.
This plunkr shows your sort code working fine. Note where the data object is declared, populated, and used.
here is a partial listing. I have added buttons that re-order data. To achieve this we need to put the ordering logic inside render rather than inside the d3.csv callback.
<script type="text/javascript">
var data = [];
d3.csv("data.csv",
function(error, rows) {
rows.forEach(function(r) {
data.push({
expense: +r.expense,
category: r.category
})
});
render();
});
function render(d3Comparator) {
if(d3Comparator) data = data.sort(function(a, b) {
return d3[d3Comparator](a.expense, b.expense);
});
d3.select("body").selectAll("div.h-bar") // <-B
.data(data)
.enter().append("div")
.attr("class", "h-bar")
.append("span");
d3.select("body").selectAll("div.h-bar") // <-C
.data(data)
.exit().remove();
d3.select("body").selectAll("div.h-bar") // <-D
.style("width", function(d) {
return (d.expense * 5) + "px";
})
.select("span")
.text(function(d) {
return d.category;
});
}
</script>
<button onclick="render('ascending')">Sort ascending!</button>
<button onclick="render('descending')">Sort descending!</button>

Format for D3's data() binding

What is the required format for things passed to d3's .data()?
In this jsfiddle, I try to create several <div> elements for each metric. Unfortunately, nothing happens. I'm assuming this is related to an incorrect data structure?
http://jsfiddle.net/GppWz/
The main issue here is that you are trying to use a hash as a data source, while d3 wants your data in array format.
If you can, modify your data source so that you are receiving data in array format. If this is not possible, you can use the d3.entries function to convert the object into an array:
var listContainers = d3.select('#lists').selectAll('div')
.data(d3.entries(data))
.enter().append('div')
.attr('class', 'listContainer');
listContainers.append('h5')
.text(function(d) {
return d.key;
});
var item = listContainers.selectAll('.item').data(function(d) {
return d.value;
}).enter()
.append('div')
.attr('class', 'item')
.text(function(d) {
return 'average_dif = ' + d.average_dif;
});
// ...

D3 - dealing with JSON nested data structures

My post is somehow a follow-up to this question : D3 - how to deal with JSON data structures?
In short, I'm in trouble with handling complex nested JSON structures.
Let me give a simple sample (that mirrors the above link) to illustrate:
var regularData = [[40,20,30,24,18,40],
[24,20,30,41,12,34]];
var myTable = d3.select("body").append("table")
.selectAll("tr")
.data(regularData, function(d) {
return d;
})
.enter()
.append("tr")
.selectAll("td")
.data(function(d) {
return d;
})
.enter()
.append("td")
.text(function(d) {
return d;
});
As already shown through D3's doc, this produces two lines of numbers, as expected.
But if I replace regularData by this:
var newData = [{"user":"jim","scores":[40,20,30,24,18,40]},
{"user":"ray","scores":[24,20,30,41,12,34]}];
and adapt the myTable's construction lines 3 to 5 with this:
.data(newData, function(d) {
return d.scores;
})
I would have expected to loop in the scores for each user. Instead, the second data clause is still bound to top-level objects (i.e. with properties "user" and "scores").
A brute-force approach would be to cast my data in a plain "array of arrays" adapted to each purpose. But maybe I missed something, and a more elegant way is possible ?
Thanks by advance for your help,
P.
You have slightly misunderstood what the second argument to d3.data is meant for.
That argument is used for object constancy by providing an id for the data and not as an accessor function, as in most other functions in the d3 API.
However, when a function is passed as the first argument, as is here:
.data(function(d) {
return d;
})
then it does behave as an accessor function.
In your case, what you want is something along these lines:
var newData = [{"user":"jim","scores":[40,20,30,24,18,40]},
{"user":"ray","scores":[24,20,30,41,12,34]}];
var myTable = d3.select("body").append("table")
.selectAll("tr")
.data(regularData, function(d) {
return d.user;
})
.enter()
.append("tr")
.selectAll("td")
.data(function(d) {
return d.scores;
})
.enter()
.append("td")
.text(function(d) {
return d;
});
I have used the user field as the id of the data and have returned the scores in the second call to data to create the td elements.

Resources