Struggling to get a chart with modified JSON format - javascript

I'm very new to d3js. I did a bar chart using one json file. It works very fine. But now I changed the JSON file format due to some unavoidable reasons.My previous json was:
[
{"name":"bike","value":98},
{"name":"car","value":52},
{"name":"bus","value":20},
{"name":"van","value":65}
]
Code is :
d3.json("sample.json", function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var chart = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = chart.selectAll(".bar")
.data(data)
.enter().append("g");
bar.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return height - y(d.value); });
});
This is my new json file:
[
{"category":"bike","bike":38,"car":0,"bus":0,"van":0},
{"category":"car","bike":0,"car": 50,"bus":0,"van":0,},
{"category":"bus","bike":0,"car": 0,"bus":14,"van":0},
{"category":"van","bike":0,"car": 0,"bus":0,"van":43}
]
I want to get a chart like same as previous one. Data with "0" (zero) shouldn't appear in chart.
Pls help me. Thanks in advance :)

You don't provide an explanation of your new format, and I'm not sure how your previous format was working (given the x attribute setting) but adjusting to the new format should be as simple as
.attr("x", function(d) { return x(d.category); })
.attr("y", function(d) { return y(d[d.category]); })
And similar changes elsewhere. (e.g. for the height)
Or maybe I'm missing something?

Related

Can't decrease number of ticks in axisBottom

I'm trying to create some bar chart with the U.S. GDP growth. In the x axis, all the YYYY-MM-DD values are shown even though I explicitly set .call(d3.axisBottom(x).ticks(10); What should I do? I tried it with d3.timeYear.every(25) too.
Here's my code:
var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json";
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json(url, function(error, data) {
if (error) throw error;
x.domain(data.data.map(function(d) { return d[0]; }));
y.domain([0, d3.max(data.data, function(d) { return d[1]; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(d3.timeYear.every(25)));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("font-size", "30px")
.attr("font-color", "black")
.attr("dy", "7.71em")
.attr("text-anchor", "end")
.text("Frequency");
g.selectAll(".bar")
.data(data.data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d[1]); });
});
You have two problems here. First, you are using a band scale, and d3.timeYear.every(25) will have no effect. But, on top on that, you're using that d3.timeYear.every(25) inside a ticks function, and that won't work.
According to the API:
This method has no effect if the scale does not implement scale.ticks, as with band and point scales. (emphasis mine)
Thus, a possible solution is filtering the band scale's domain inside a tickValues function.
In this example, I'm filtering one tick out of every 20. Also, I'm splitting the string to show only the year:
d3.axisBottom(x).tickValues(x.domain().filter(function(d, i) {
return !(i % 20);
})).tickFormat(function(d) {
return d.split("-")[0]
});
Here is your code with that change:
var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json";
var svg = d3.select("svg"),
margin = {
top: 20,
right: 10,
bottom: 30,
left: 50
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand().range([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
d3.json(url, function(error, data) {
if (error) throw error;
x.domain(data.data.map(function(d) {
return d[0];
}));
y.domain([0, d3.max(data.data, function(d) {
return d[1];
})]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickValues(x.domain().filter(function(d, i) {
return !(i % 20);
})).tickFormat(function(d) {
return d.split("-")[0]
}))
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("font-size", "30px")
.attr("font-color", "black")
.attr("dy", "7.71em")
.attr("text-anchor", "end")
.text("Frequency");
g.selectAll(".bar")
.data(data.data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d[0]);
})
.attr("y", function(d) {
return y(d[1]);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d[1]);
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="400"></svg>

D3 v4 line chart with JSON dates

I've worked on this for a few days now, and I cant really get my line drawn and got some date formating problems I can't crack.
Using this D3 v3 fiddle as inspiration: http://jsfiddle.net/vmvp0zja/ I tried to convert it to D3 v4, but I can't really get my data drawn properly.
I am trying to draw several lines, but I cant even draw one..
Could you take a look and see what I am missing here? Thanks! :)
// JSON data:
var data = [
{"Date":"\/Date(1475272800000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475359200000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475445600000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475532000000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475618400000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475704800000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475791200000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475877600000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475964000000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476050400000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476136800000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476223200000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476309600000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476396000000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475445600000)\/","Open":1,"Closed":0},
{"Date":"\/Date(1475532000000)\/","Open":1,"Closed":0},
{"Date":"\/Date(1475618400000)\/","Open":2,"Closed":0},
{"Date":"\/Date(1475791200000)\/","Open":9,"Closed":0},
{"Date":"\/Date(1475964000000)\/","Open":1,"Closed":0},
{"Date":"\/Date(1475445600000)\/","Open":0,"Closed":1},
{"Date":"\/Date(1475532000000)\/","Open":0,"Closed":1},
{"Date":"\/Date(1475618400000)\/","Open":0,"Closed":1},
{"Date":"\/Date(1475964000000)\/","Open":0,"Closed":1}]
This is my D3 mess:
// linechart.js
var formatTime = d3.timeFormat("%Y-%m-%d");
data.forEach(function (d) {
var unixToISO = new Date(d.Date.match(/\d+/)[0]*1);
d.Date = formatTime(unixToISO);
d.Open = +d.Open;
d.Closed = +d.Closed;
console.log(d.Date);
return d;
});
var margin = {top: 30, right: 40, bottom: 30, left: 50 },
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var x = d3.scaleTime()
.range([0, width]);
var y0 = d3.scaleLinear()
.range([height, 0]);
// Scale the range of the data
x.domain(d3.extent(data, function (d) { return d.Date; }));
y0.domain([
d3.min(data, function (d) { return Math.min(d.Open); }),
d3.max(data, function (d) { return Math.max(d.Open); })]);
var valueline1 = d3.line()
.x(function (d) { console.log(x(d.Date)); return x(d.Date); })
.y(function (d) { return y(d.Open); });
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
svg.append("g")
.attr("class", "y axis")
.style("fill", "steelblue")
.call(d3.axisLeft(y0));
svg.append("path") // Add the valueline path.
.data(data)
.attr("class", "line")
.attr("d", valueline1);
Help much appreciated in advance. I am probably overlooking something obvious.
Thank you!
Changes:
data.forEach(function(d) {
var unixToISO = new Date(d.Date.match(/\d+/)[0] * 1);
d.Date = unixToISO; // here
d.Open = +d.Open;
// (...)
var valueline1 = d3.line()
.x(function(d) {
console.log(x(d.Date));
return x(d.Date);
})
.y(function(d) {
return y0(d.Open); // here
});
// (...)
svg.append("path")
.data([data]) // here
.attr("class", "line")
.attr("d", valueline1);
and added x-axis labels with
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Date");
BTW, your data is unsorted.
JSFiddle Demo
Docs reference

How to get second bars to show on grouped bar chart?

I'm trying to get a second bar into my graph. The elements are correctly getting appended but not in the correct location and not the correct height. What I want from the data to be at the 1 position in the x-axis to have 2 bars one with a height of 2 and the other height of 3 and so on.
http://jsfiddle.net/626uesbh/4/
var svg2 = d3.select("#histogram").append("svg2")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg2.selectAll(".rect")
.data(data)
.enter().append("g")
.attr("transform", "translate(0, 100)")
.append("rect")
.attr("class", "rect")
.attr("width", 10)
.attr("height", function(d) { return height - y2Map(d); })
.attr("x", xMap)
.attr("y", yMap)
.style("fill", "blue");
I suspect svg2 transform is the problem but after trying fiddling with it for an hour I can seem to get what I want. I looked at this question and tried to implement it into my problem. D3.js grouped bar chart
Since each element in your data contains the values for both bars, you have to add them as a group. That is, add a 'g' element to the chart for each element in the array, then add a bar for inner_array[1] and inner_array[2].
Hopefully this gets you on the right path, essentially all I changed was the stuff after your //bar comment.
http://jsfiddle.net/626uesbh/6/
// bar
var bar_groups = svg.selectAll('.bar-group')
.data(data)
.enter().append('g')
.attr('class', 'bar-group');
bar_groups.append('rect')
.attr("class", "rect")
.attr("width", 10)
.attr("height", function(d) { return height - yScale(d[1]); })
.attr("x", function(d) {
return xScale(d[0]) - 5;
})
.attr("y", function(d) {
return yScale(d[1]);
})
.style("fill", "green");
bar_groups.append('rect')
.attr("class", "rect")
.attr("width", 10)
.attr("height", function(d) { return height - yScale(d[2]); })
.attr("x", function(d) {
return xScale(d[0]) + 5;
})
.attr("y", function(d) {
return yScale(d[2]);
})
.style("fill", "blue");
Note: there are much more elegant ways to do this. I am only showing you how to add the bars to your existing code. Please take a look at http://bl.ocks.org/mbostock/3887051 for further guidance.

Horizontal bar chart d3.js not showing label

I've an horizontal bar chart in d3.js and I would like to add the name like "y-label" for every bar of the chart.
The original example of my bar chart is http://bl.ocks.org/mbostock/2368837
without negative values.
So I modified it for my purpose
var margin = {top: 40, right: 20, bottom: 100, left: 60},
width = 720 - margin.left - margin.right,
height = 480 - margin.top - margin.bottom;
var x_4 = d3.scale.linear()
.range([0, width])
var y_4 = d3.scale.ordinal()
.rangeRoundBands([0, height], .2);
var xAxis_4 = d3.svg.axis()
.scale(x_4)
.orient("top");
var tip_4 = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Value:</strong> <span style='color:red'>" + d.ln_numfollowers + "</span>";
})
var sampleSVG_4 = d3.select("#LinkedIn").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(tip_4);
d3.csv("#routes.Assets.at("d3/linkedin_competitor_prova.csv")", type, function(error, data) {
x_4.domain(d3.extent(data, function(d) { return d.ln_numfollowers; })).nice();
y_4.domain(data.map(function(d) { return d.organization_name; }));
sampleSVG_4.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) { return d.ln_numfollowers < 0 ? "bar negative" : "bar positive"; })
.attr("x", function(d) { return x_4(Math.min(0, d.ln_numfollowers)); })
.attr("y", function(d) { return y_4(d.organization_name); })
.attr("width", function(d) { return Math.abs(x_4(d.ln_numfollowers) - x_4(0)); })
.attr("height", y_4.rangeBand())
.on('mouseover', tip_4.show)
.on('mouseout', tip_4.hide);;
sampleSVG_4.append("g")
.attr("class", "x axis")
.call(xAxis_4);
sampleSVG_4.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x_4(0))
.attr("x2", x_4(0))
.attr("y2", height)
});
function type(d) {
d.ln_numfollowers = +d.ln_numfollowers;
return d;
}
The csv data file is:
organization_name,ln_numfollowers
Carrot.mx,100
CarJump,45
I don't know why the organization_name is not showing.
As you can see, not even in the original example the label on the y axis are showing.
Couple of problems:
1.) You probably don't want to create your x-axis using extent. With your sample data this would create a chart from 45 to 100. You probably want to start it at zero.
x_4.domain([0,d3.max(data, function(d) { return d.ln_numfollowers; })]);
2.) You don't actually create a conventional y-axis. This code:
sampleSVG_4.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x_4(0))
.attr("x2", x_4(0))
.attr("y2", height)
Is creating a y-axis that's just a line. It's not using the built-in d3axis creation. What you need is:
var yAxis_4 = d3.svg.axis()
.scale(y_4)
.orient("left");
....
sampleSVG_4.append("g")
.attr("class", "y axis")
.call(yAxis_4);
Example here.

Change data source on click

I am new to d3.js and I am trying to make a chart where people could change the data source on click. To give a concrete example, if I display a country's population over time, I'd like to have a button for each country and when people click on it, it updates the source.
My initial chart is defined here:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "%");
var svg = d3.select(".chart-container").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("./javascripts/data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
});
function type(d) {
d.frequency = +d.frequency;
return d;
}
And when people click on a button, I'd like to change the source:
function updateData() {
// Get the data again
d3.tsv("./javascripts/data2.tsv", type, function(error, data) {
console.log(data);
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Select the section we want to apply our changes to
var svg = d3.select(".chart-container").transition();
// Make the changes
svg.selectAll(".bar")
.data(data) // here I get: Uncaught TypeError: undefined is not a function
.exit().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); });
});
}
However this is not working. I get the error commented in the code.
What I am doing wrong?
Cheers

Categories