1
votes

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.

2

2 Answers

1
votes

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 */
}
0
votes

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}
];