42

Most of the examples in gallery load data from TSV files.

How can I convert the following to use a local json variable instead of TSV data?

d3.tsv("data.tsv", function(error, data) {

    var myEntitiesJson = getEntitiesJson(); // <------ use this instead of "data"
    data.forEach(function(d) {
        d.frequency = +d.frequency;
    });

    x.domain(data.map(function(d) { return d.letter; }));
    y.domain([0, d3.max(data, function(d) { return d.frequency; })]);

    ...

    svg.selectAll(".bar")
        .data(data)     // <----- bind to myEntities instead
}

As far as I can tell, I just need to do something to my entitiesJson, in order to data-fy it so that the chart could bind to it.

UPDATE

I am making some progress. I plugged in my entities from JSON and the graph is starting to take new shape.

Currently the following code breaks:

svg.selectAll(".bar")
    .data(myEntities)  // <-- this is an array of objects
    .enter().append("rect")

This is causing:

Error: Invalid value for attribute y="NaN"

Error: Invalid value for attribute height="NaN"

4
  • stackoverflow.com/questions/20940854/… has an answer. Commented Apr 8, 2014 at 15:42
  • hey, Cees, that seems like a specific solution for collapsible tree? Commented Apr 8, 2014 at 19:59
  • That uses a local JSON variable instead of an external source. Commented Jul 21, 2014 at 17:57
  • It would also help to post the problematic JSON. Commented Jul 21, 2014 at 18:05

6 Answers 6

24

for remote data.json replace :

d3.tsv("data.tsv", function(error, data) {...}

with :

d3.json("data.json", function(error, data) {
    console.log(data); // this is your data
});

for local data:

var myData = { {date:'2013-05-01', frequency:99},
               {date:'2013-05-02', frequency:24} };

function draw(data) {
    console.log(data); // this is your data
}

draw(myData);
Sign up to request clarification or add additional context in comments.

2 Comments

so, in the d3 example i'm following, it looks like chart axis is mapped to the dataset using x.domain(data.map(...)), and then svg.selectAll(".bar").data(data).. it's not clear from your example how i can retrofit a jason snippet in place of how data is used. how would draw(myData) magically know which properties to use for which axis etc?
Replace console.log(data); // this is your data with your function(error, data) body, like x.domain(data.map(function(d) { return d.letter; })) etc.
8

There isn't a simple way to data-fy any given json, because not all json objects are the same shape.

By shape, I mean the way that the data is organized. For example, both '{"foo" : 1, "bar" : 2}' and '{"names" : ["foo", "bar"], "values" : [1, 2]}' could be used to store the same data, but one stores everything in an object in which the object keys correspond to the names of data points, and one uses separate arrays to store names and values, with corresponding entries having a common array index.

There is, however, a general process you can go through to turn json into data. First, you'll need to parse your json. This can be done with the javascript-standard JSON object. USe JSON.parse(myJson) to obtain data from your json object if it's already uploaded to the client. d3.json(my/json/directory, fn(){}) can both load and parse your json, so if you're loading it from elsewhere on your server, this might be a better way to get the json into an object.

Once you have your json packed into a javascript object, you still need to data-fy it, which is the part that will depend on your data. What d3 is going it expect is some form of array: [dataPoint1, dataPoint2, ...]. For the two examples I gave above, the array you would want would look something like this:

[{'name' : 'foo', 'value' : 1}, {'name' : 'bar', 'value' : 2}]

I've got one element in my array for each data point, with two attributes: value and name. (In your example, you would want the attributes letter and frequency)

For each of my examples, I would use a different function to create the array. With this line in common:

var rawData = JSON.parse(myJson);

My first json could be packed with this function:

var key;
var data = [];

for(key in rawData){
    if(rawData.hasOwnProperty(key)){
        data.push({'name' : key, 'value' : rawData[key]});
    }
}

For the second example, I would want to loop through each attribute of my object, names, and values. My code might look like this:

var i;
var data = [];

for(i = 0; i < rawData.names.length; i++){
    data.push({'name' : rawData.names[i], 'value' : rawData.values[i]});
}

Both of these will yield a data-fied version of my original JSON that I can then use in d3.

5 Comments

thanks for the reply. I haven't used d3 in a while but plan on getting back to it shortly and will give this strategy a try!
As someone who has been reading all of the different tutorials about mapping json data to d3, this is by far the most informative piece I've seen yet. +1 from me.
Hi! i am facing similar problem when, I am trying to read data from variable instead of reading from "json file". for the following example. bl.ocks.org/mbostock/5944371 I successfully changes this in case data is simple using, something like this... for(i = 0; i < data.length; i++){ } but here it is little complicated.
@amarinderthind What you'll need to do will depend on the structure of your data. In general, with something like the partition layout, you'll need to provide a function to the "children" method on the layout function which will return the children for a given node when provided that node. Feel free to post a different question with the specifics of your problem if you need help figuring out how to do that.
@ckersch, i completed there was problem in defining root.... but now done....in following way... in case other needs..... function draw(data) { root = data; console.log("data"); // this is your data partition .value(function(d) { return d.size; }) .nodes(root) .forEach(function(d) { d._children = d.children; d.sum = d.value; d.key = key(d); d.fill = fill(d); }); } draw(data);
5

For D3js v2 or v3 (not sure which one).

Declare your dataset

var dataset = { 
    "first-name": "Stack",
    "last-name": "Overflow",
}; // JSON object
var dataset = [ 5, 10, 15, 20, 25 ]; // or array

As stated by the doc, you can use either:

an array of numbers or objects, or a function that returns an array of values

Bind it

d3.select("body").selectAll("p")
    .data(dataset)
    .enter()
    .append("p")
    .text("New paragraph!");

More explanation at Scott Murray's D3's tutorial#Binding data.

The data() function apply to a selection, more information can be found in the official documentation: selection.data([values[, key]]).

3 Comments

in the example i provided, there is mapping being done between the dataset (from data.tsv) and axis. simply substituting my json object as data is breaking for multiple reasons. it does not support .map property, and also binding it using .data() throws an exception "cannot read property 'length' of undefined".. this is why i made a comment about having to do something to my object to make it mappable to the chart. that's what i am not clear about, specifically in the example above.
This example won't work, at the moment. The first example given defines dataset as an object, not an array. d3 needs data formatted as an array, so the dataset object needs to be converted to an array before binding it to the selection.
@ckersch this answer was using d3js v2 or v3 (not sure), there is a new v4 available so the API has change. Would you mind posting a valid answer for latest version?
1

You can change the json into a javascript file that assigns the data to a global value. Taking https://bl.ocks.org/d3noob/5028304 as an example:

From:

<script>
.....
// load the data
d3.json("sankeygreenhouse.json", function(error, graph) {

    var nodeMap = {};
    graph.nodes.forEach(function(x) { nodeMap[x.name] = x; });

To:

<script src="graphData.js"></script>
<script>
.....
    var nodeMap = {};
    graph.nodes.forEach(function(x) { nodeMap[x.name] = x; });

Note that we've removed the need for the callback.

The json file was "sankeygreenhouse.json":

{
"links": [
  {"source":"Agricultural Energy Use","target":"Carbon Dioxide","value":"1.4"},

Now, in "graphData.js":

var graph = {
   "links": [
      {"source":"Agricultural Energy Use","target":"Carbon Dioxide","value":"1.4"},

Comments

0

Just change data to an array of objects like this:

let data = [{"apples":53245,"oranges":200},{"apples":28479,"oranges":200},{"apples":19697,"oranges":200},{"apples":24037,"oranges":200},{"apples":40245,"oranges":200}]

and comment out the d3.tsv("data.tsv", function(error, data) {...

Comments

-2

Why not simply transform your json to tsv as described by Rob here?

d3 expects the data or, said in another way, needs the data in a particular format: tsv.The easiest way to resolve your problem is simply formatting your data from json to tsv, which can be done easily using Rob's comments.

2 Comments

d3 does not need data formatted as tsv, it needs data formatted as an array. It contains a utility function for converting tsv data into arrays, but it's simpler to convert JSON directly into a javascript array instead of converting it to tsv and then converting the tsv to an array.
Json is a good standard, and probably the OP needs answer so that he wants to use JSON.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.