I am trying to read in data from csv file and want to visualise this data with a scatterChart in NVD3.
I would have linked to a JSfiddle or something similar but I don't know how to include a csv file in these online JavaScript IDEs. Is that possible?
The csv file has the following format:
country,y,x
Algeria,91.8,15.7
Bahrain,98.2,49.3
Jordan,99.1,55.0
Kuwait,98.6,57.4
Lebanon,98.7,58.6
My best guess for the code to read the csv file with is:
var scatterdata = [
{
key : "Group1",
values : []//{x:"",y:""}
}
];
d3.csv("literacyScatterCountrynames.csv", function (error, csv) {
if (error) return console.log("there was an error loading the csv: " + error);
console.log("there are " + csv.length + " elements in my csv set");
scatterdata[0].values["x"] = csv.map(function(d){return [+d["x"] ]; });
scatterdata[0].values["y"] = csv.map(function(d){return [+d["y"] ]; });
I see my data in the DOM and it looks about right but the chart is not shown and instead it says 'No Data Available.' in bold letters where the chart should be.
Neither here at StockOverflow, nor in the NVD3 documentation on Github, nor in the helpful website on NVD3 charts by cmaurer on GitHub could I find more information on how to do this.
Turning your csv into JSON would work, but isn't necessary. You've just got your data formatting methods inside-out.
You seem to be expecting an object containing three arrays, one for each column in your table. The D3 methods create (and the NVD3 methods expect) an array of objects, one for each row.
When you do
scatterdata[0].values["y"] = csv.map(function(d){return [+d["y"] ]; });
You're creating named properties of the values array object (all Javascript arrays are also objects), but not actually adding content using array methods, so the length of that array is still zero and NVD3 sees it as an empty array -- and gives you the "no data" warning.
Instead of using the mapping function as you have it, you can use a single mapping function to do number formatting on the data array, and then set the result directly to be your values array.
Like so:
var scatterdata = [
{
key : "Group1",
values : []//{x:"",y:""}
}
];
d3.csv("literacyScatterCountrynames.csv", function (error, csv) {
if (error) return console.log("there was an error loading the csv: " + error);
console.log("there are " + csv.length + " elements in my csv set");
scatterdata[0].values = csv.map(function(d){
d.x = +d.x;
d.y = +d.y;
return d;
});
console.log("there are " + scatterdata[0].values.length + " elements in my data");
//this should now match the previous log statement
/* draw your graph using scatterdata */
}
The mapping function takes all the elements in the csv array -- each one of which represents a row from your csv file -- and passes them to the function, then takes the returned values from the function and creates a new array out of them. The function replaces the string-version of the x and y properties of the passed in object with their numerical version, and then returns the correctly formatted object. The resulting array of formatted objects becomes the values array directly.
Edit
The above method creates a single data series containing all the data points. As discussed in the comments, that can be a problem if you want a category name to show up in the tooltip -- the NVD3 tooltip automatically shows the series name as the tooltip value. Which in the above code, would mean that every point would have the tooltip "Group1". Not terribly informative.
To format the data to get useful tooltips, you need each point as its own data series. The easiest way to make that happen, and have the output in the form NVD3 expects, is with d3.nest. Each "nested" sub-array will only have one data point in it, but that's not a problem for NVD3.
The code to create each point as a separate series would be:
var scatterdata;
//Don't need to initialize nested array, d3.nest will create it.
d3.csv("literacyScatterCountrynames.csv", function (error, csv) {
if (error) return console.log("there was an error loading the csv: " + error);
console.log("there are " + csv.length + " elements in my csv set");
var nestFunction = d3.nest().key(function(d){return d.country;});
//create the function that will nest data by country name
scatterdata = nestFunction.entries(
csv.map(function(d){
d.x = +d.x;
d.y = +d.y;
return d;
})
); //pass the formatted data array into the nest function
console.log("there are " + scatterdata.length + " elements in my data");
//this should still match the previous log statement
//but each element in scatterdatta will be a nested object containing
//one data point
/* draw your graph using scatterdata */
}
You could place the data into a variable, as Mike describes here:
name value
Locke 4
Reyes 8
Ford 15
Jarrah 16
Shephard 23
Kwon 42
is represented this way:
var data = [
{name: "Locke", value: 4},
{name: "Reyes", value: 8},
{name: "Ford", value: 15},
{name: "Jarrah", value: 16},
{name: "Shephard", value: 23},
{name: "Kwon", value: 42}
];
Related
I am trying to create a simple "plug-n-play" map template, that allows user to put a csv file with geoids and values and then see the values as a choropleth.
Right now I am merging two datasets (map and values) using double loop, but wondering if there is any other option:
This chunk of code stays within the function that loads geodata (fresh_ctss) :
d3.csv("data/communities_pop.csv", function(error, comms)
{
csv = comms.map(function(d)
{
//each d is one line of the csv file represented as a json object
// console.log("Label: " + d.CTLabel)
return {"community": d.community, "population" :d.population,"label": d.tract} ;
})
csv.forEach(function(d, i) {
fresh_ctss.forEach(function(e, j) {
if (d.label === e.properties.geoid) {
e.properties.community = parseInt(d.community)
e.properties.population = parseInt(d.population)
}
})
})
You'll definitely need two loops (or a nested loop) - the most optimal way would be to just limit how much iteration needs to happen. Right now, the first loop goes through every csv row. The following nested loop goes through every csv row (as new different object) and then, as many times as there are rows in the csv, through every item in fresh_ctss.
If you mapped the rows into an object instead of an array, you could iterate through the rows once (total) and then once through the elements of fresh_ctss (again, total). Code below assumes that there are no tract duplicates in comms:
all_comms = {}
comms.forEach(function(d) {
all_comms[d.tract] = {"community": d.community, "population": d.population}
})
fresh_ctss.forEach(function(e) {
comm = all_comms[e.properties.geoid]
e.properties.community = parseInt(comm.community)
e.properties.population = parseInt(comm.population)
}
I'm trying to perform what seems like a simple task and display <li>'s for each item in a JSON object. Then, when I click each displayed item, get additional JSON info from all the selected items, and create an array that is stored in a variable for use later.
Here is my JSON. Nothing crazy, simple array of data.
[
{
"subCategory": "Foo",
"total": 100,
"converted": 25,
"revenue": 500
},
{
"subCategory": "Bar",
"total": 100,
"converted": 25,
"revenue": 1000
}
]
With my JS (using jQuery), I'm getting and displaying each item correctly, but I'm not getting the data that I want when i select an item that is getting displayed. In the following JS, I realize I'm not doing much beyond adding the class to the li displayed, but I'm stuck on how to go about getting the data for each selected item and over write the variables below.
(function(){
$('.bubbles').on('click', 'li', function(){
$(this).toggleClass('selected');
});
$.getJSON('data.json', function(data) {
$.each(data,function(i, value){
// Display bubbles
$('.marketingBubbles').append("<li class='"+ value.leads + " "+ value.status + " "+ value.lift + "'>");
// Get data and create arrays
var totalArray = [value.total], // Get values from all totals
convertedArray = [value.converted], // Get all values from converted
revenueArray = [value.revenue]
// Add up numbers in arrays
var totalConverted = eval(convertedArray.join('+')), // Sum of all converted leads
totalLeads = eval(totalArray.join('+')), // Sum of all total leads
totalRevenue = eval(indicatorRevenue.join('+')) // Sum of all revenue
});
});
})();
HTML
<ul class="marketingBubbles"></ul>
Eventually, i would like to take the values within an array, no matter how many times it is changed, perform some simple math operations with the data and display in various divs on the screen.
Any help would be appreciated.
Thank you!
This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 9 years ago.
I have entered the following code in the Chrome console:
d3.csv("dataset32.txt")
.row(function(d) { return {time: +d.time, value: +d.val}; })
.get(function(error, rows) { console.log(rows); });
This returns an array of 160 objects which has the data of my CSV file.
How do I reference the data in these objects?
The "dataset32.txt" is CSV data which looks like this:
time,val
0,1.8988762857143
0.0625,0
0.125,-2.6204492857143
0.1875,-0.34179771428571
The console prints out the result of the above commands as follows:
[Object, Object, Object…]
[0 … 99]
0: Object
time: 0
value: 1.8988762857143
__proto__: Object
So how do I reference the data inside these objects: "time" and "value"?
Here's a straight forward method for importing your csv file using d3.
d3.csv("path to the csv", function(data, error) { }).
More info about csv.
Here's a function that's help you.
Step 1 : Importing your CSV file.
d3.csv("path to your csv file", function(data, error) {
// You're function goes in here.
})
Step 2 : Targeting your data.
// This can be done using the data variable assigned in the function.
data.forEach(function(d) {
// Function goes in here.
})
Step 3 : Targeting your columns.
// Suppose you have 2 columns namely time and val.
data.forEach(function(d) {
// You can target your columns using this fucntion.
// function(d) {return d.colname}
// Here I'm parsing your time and val col numbers to actual numbers after importing.
d.time = +d.time;
d.val = +d.val;
})
Documentation for d3.
Hope this helps.
I'm trying to read in a csv file with D3 and I'm a little stuck. The way my csv file is formatted is that the first line is a merged cell containing a year then the next line will contain the data descriptions (name, age etc).
Currently I have the following:
var resourceList = [{description: "All Yearly Data",
name: "yearlyData",
path: "data.csv"};
d3.csv(resourceInfo.path, function(error, d) {
theData.resources[resourceInfo.name].processed = true;
theData.resources[resourceInfo.name].error = error;
theData.resources[resourceInfo.name].data = d;
theData.numProcessed += 1;
});
This reads the first line in as the data descriptions and then the following lines as actual data. What I want to do is have an multidimensional array which I could go through by year. Is it possible to skip lines while parsing to make sure I can manage that or no?
Thanks!
one way of getting at this would be to use filter:
d3.csv(resourceInfo.path, function(error, d) {
var newData = d.filter(function(obs) { return INSERT YOUR FILTER CONDITION HERE;});
...
see also:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2Ffilter
imagine that you have csv data like this that I can read them from a textarea on my web page:
A,10,USA
B,5,UK
A,2,USA
I am trying to use cs-jQuery to parse and process this data to get the following report:
A has ran 12 miles with average of 6.
B has ran 5 miles with average of 5.
The code that I have written looks like this:
<script>
$(document).ready(function() {
$('#calculate').click(function() {
$('#report').empty();
var data = $('#input').val();
var values = $.csv.toObjects(data);
$('#report').append(values);
alert(values);
});
});
</script>
but all I am getting is [object Object] [object Object]...
any suggestion on what I should do? any way to do this with jQuery functionality?
this function $.csv.toObjects() return array of objects
Useful for parsing multi-line CSV data into an array of objects
representing data in the form {header:value}. Unless overridden, the
first line of data is assumed to contain the headers.
You don't have header so you should use $.csv.toArrays() instead and iterate over that array:
$.each($.csv.toArrays(data), function(_, row) {
$('#report').append('<div>' + row[0] + ' has ran ' + row[1] + ' miles</div>');
});
if you want to use toObjects you need to put header
person,miles,country
A,10,USA
B,5,UK
A,2,USA
and access it using row.person row.miles and row.country