parsing json to bar chart d3js - javascript

I'm trying to create a bar chart using the json url. With respect to impressions and time. I don't think I'm referencing the data correctly at .data(data) how do I access the impressions field from json file in d3?
var url = "https://script.google.com/macros/s/AKfycbwy56QiQwyfkkaLFWZ33QHVieAHhtLJYNa_AzKcCBr-J7Catgv2/exec?id=1vQsWQPUET20KcgeRKgs5NOOBngqLeUuNTHI1bWi5Et8&sheet=Sheet1";
d3.json(url, function(data) {
console.log(data.Sheet1[0]);
var canvas = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500)
canvas.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", function(d) {
return d.Impressions
})
.attr("height", 45)
.attr("y", function(d, i) {
return i * 50
})
.attr("fill", "blue")
});

You have to pass an array to the function data(). Thus, since data is an object:
Object {Sheet1: Array[71]}
Your data should be data.Sheet1 instead. Check the demo:
var url = "https://script.google.com/macros/s/AKfycbwy56QiQwyfkkaLFWZ33QHVieAHhtLJYNa_AzKcCBr-J7Catgv2/exec?id=1vQsWQPUET20KcgeRKgs5NOOBngqLeUuNTHI1bWi5Et8&sheet=Sheet1";
d3.json(url, function (data) {
var scale = d3.scale.linear()
.domain([0, d3.max(data.Sheet1, function(d){ return d.Impressions})])
.range([0, 500]);
var canvas = d3.select("body").append("svg")
.attr("width",500)
.attr("height",500)
canvas.selectAll("rect")
.data(data.Sheet1)
.enter()
.append("rect")
.attr("width",function(d){return scale(d.Impressions)})
.attr("height", 6)
.attr("y",function(d,i){return i*7})
.attr("fill","blue")
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
PS: I added a scale to your code.

Related

Conditional Bar Chart with D3

I have a D3 visualization with a map and a bar chart. I am trying to get the bar chart to change depending on which circle on the map is clicked. Not sure how to do this. I have a function in my bar_chart.js file named update(newData) and a few extra arrays for the different circles on the map. Here is the link to the bl.ocks for the map and bar char.
js code for map
var myData = [21, 3, 5, 21, 15];
//Width and height
var w = 200;
var h = 125;
var yScale = null;
function draw(initialData) {
var xScale = d3.scale.ordinal()
.domain(d3.range(initialData.length))
.rangeRoundBands([0, w], 0.05);
yScale = d3.scale.linear()
.domain([0, d3.max(initialData)])
.range([0, h]);
//Create SVG element
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(initialData)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", "steelblue");
svg.selectAll("text")
.data(initialData)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
}
draw(myData);
//update function
function update(newData) {
yScale.domain([0, d3.max(newData)]);
var rects = d3.select("#chart svg")
.selectAll("rect")
.data(newData);
// enter selection
rects
.enter().append("rect");
// update selection
rects
.transition()
.duration(300)
.attr("y", function(d) {
return h - yScale(d);
})
.attr("height", function(d) {
return yScale(d);
})
// exit selection
rects
.exit().remove();
var texts = d3.select("#chart svg")
.selectAll("text")
.data(newData);
// enter selection
texts
.enter().append("rect");
// update selection
texts
.transition()
.duration(300)
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.text(function(d) {
return d;
})
// exit selection
texts
.exit().remove();
}
var mk = [10,17,20,14,8];
var cn = [18,4,9,20,15];
var nd = [5,12,7,15,21];
d3.select("#update").on("click", function() { update(newData); });
You have to incorporate the barchart data in your cities.csv file.
In the on-click handler of cities.csv where you show the tooltip you have to transform the data from the CSV into an array and call the bar chart update() method with this array.
One way of doing is to replace the , from the bar chart data with another char and split the string and convert the parts to numbers.
var cityData = d.barchart.split('#').map(Number);
update(cityData);
You also have to set the attributes of the new rects and texts of the bar chart. And the x-position will change if the number of bars change.

How do you reference an API and use it as data in D3

I want to make a bar chart using an API that pulls a JSON, but am not sure how to go about referencing the data.
First point of data is a name that is supposed to be the category, and the second is the actual data value that the bar will use to determine its height. For example:
Ralph 92
Jim 34
Michelle 55
var Champs;
function callback(response) {
Champs = response;
console.log(Champs);
}
var url =
"https://ddragon.leagueoflegends.com/cdn/4.7.16/data/en_US/champion.json";
d3.json(url, function(data) {
callback(data);
Object.keys(Champs.data).forEach(function(key, index) {
var champsObj = key;
var champName = Champs.data[champsObj].name;
var champHP = Champs.data[champsObj].stats.hp;
})
var scale = d3.scaleLinear()
.domain([0, d3.max(data.data, function(d){ return d.data.stats.hp})])
.range([0, 500]);
var canvas = d3.select('body').append('svg')
.attr('width', 500)
.attr('height', 500)
canvas.selectAll('rect')
.data(data.data)
.enter()
.append('rect')
.attr('width', function(d){return scale(d.data.stats.hp)})
.attr('height', 6)
.attr('y', function(d, i){return i*7})
.attr('fill', 'blue')
});
I searched through the documentation as well as a d3wiki for the answers, and found a bit of help from a similar question here but I'm still really struggling with how to reference the data in the json
data.data here is not an array but an object, call Object.values(data.data) to get the array with the values.
function callback(response) {
Champs = response;
console.log(Champs);
}
var url =
"https://ddragon.leagueoflegends.com/cdn/4.7.16/data/en_US/champion.json";
d3.json(url, function(data) {
callback(data);
var scale = d3.scaleLinear()
.domain([0, d3.max(Object.values(data.data), function(d){ return d.stats.hp})])
.range([0, 500]);
var canvas = d3.select('body').append('svg')
.attr('width', 500)
.attr('height', 500)
canvas.selectAll('rect')
.data(Object.values(data.data))
.enter()
.append('rect')
.attr('width', function(d){return scale(d.stats.hp)})
.attr('height', 6)
.attr('y', function(d, i){return i*7})
.attr('fill', 'blue')
});

D3.js Enter, Update, Exit issue

I have a relatively simple barchart. I want to .transition() between datasets with an .on("click") event. What I'm getting is a complete redraw of an additional chart appended to the DOM id, instead of removing the original chart and transitioning or replacing it. I think I'm misunderstanding how to correctly .remove().
d3.json("data/cfilt-steps.json", function(d) {
d.forEach(function(d) {
parseDate = d3.time.format("%Y-%m-%d").parse;
d.date = parseDate(d.date); d.value = +d.value;
});
margin = {top:5, right:5, bottom: 40, left:5},
height = 150 - margin.top - margin.bottom,
width = 500 - margin.left - margin.right,
barPadding = 1;
steps = crossfilter(d),
monthdim = steps.dimension(function(d){ thisDate = new Date(d.date); return thisDate.getMonth(); }),
monthgrp = monthdim.group().reduceSum(function(d){ return d.value; });
daydim = steps.dimension(function(d){ thisDate = new Date(d.date); return thisDate.getDay(); }),
daygrp = daydim.group().reduceSum( function(d) { return d.value; });
stepColor = d3.scale.threshold()
.domain([100, 150, 200, 250, 300, 400, 500])
.range(["#E3E3E3", "#D0DFD2", "#C3DABC", "#BDCB87", "#CAB44E", "#E29517", "#FF6600"]);
d3.select("#monthly-steps-previous-selector")
.on("click", function(d) {reDraw(monthgrp.all()) })
d3.select("#monthly-steps-next-selector")
.on("click", function(d) {reDraw(daygrp.all()); })
function reDraw(data) {
xScale = d3.scale.ordinal().domain(monthdim).range(0, width);
yScale = d3.scale.linear().domain([0, d3.max(data, function(d){return d.value;})]).range([height, 5]);
var stepbars = d3.select("#steps-bar")
.append("svg:svg")
.attr("width", width)
.attr("height", height)
stepbars.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d,i){ return i * width/data.length; })
.attr("y", function(d){ return yScale(d.value); })
.attr("width", width/data.length - barPadding)
.attr("height", function(d) { return height-yScale(d.value); })
.attr("fill", function(d){ return stepColor(d.value/2000); })
}
reDraw(monthgrp.all());
});
Can someone show me what this is supposed to look like, or tell me what I'm doing wrong?
Your reDraw function appends the svg, this means every time you call redraw a new svg is appended, hence the double chart. I would suggest to put the lines
var stepbars = d3.select("#steps-bar")
.append("svg:svg")
.attr("width", width)
.attr("height", height)
above the reDraw function.
Furthermore, your redraw function does not call remove. I would do something like:
//Select and bind to data
var selection = stepbars.selectAll("rect")
.data(data);
//Enter and create new rectangles
selection.enter()
.append("rect");
//Update all rectangles
selection.attr("x", function(d,i){ return i * width/data.length; })
.attr("y", function(d){ return yScale(d.value); })
.attr("width", width/data.length - barPadding)
.attr("height", function(d) { return height-yScale(d.value); })
.attr("fill", function(d){ return stepColor(d.value/2000); });
//Remove unused rectangles
selection.exit().remove();

D3 updating bar chart with new data set cause update issue

Well the rendering of bar chart works fine with default given data. The problem occurs on the button click which should also cause the get of new data set. Updating the x-axis y-axis works well but the rendering data causes problems.
First Ill try to remove all the previously added rects and then add the new data set. But all the new rect elements gets added into wrong place, because there is no reference to old rects.
Here is the code and the redraw is in the end of code.
http://jsfiddle.net/staar2/wBNWK/9/
var data = JSON.parse('[{"hour":0,"time":147},{"hour":1,"time":0},{"hour":2,"time":74},{"hour":3,"time":141},{"hour":4,"time":137},{"hour":5,"time":210},{"hour":6,"time":71},{"hour":7,"time":73},{"hour":8,"time":0},{"hour":9,"time":68},{"hour":10,"time":70},{"hour":11,"time":0},{"hour":12,"time":147},{"hour":13,"time":0},{"hour":14,"time":0},{"hour":15,"time":69},{"hour":16,"time":67},{"hour":17,"time":67},{"hour":18,"time":66},{"hour":19,"time":0},{"hour":20,"time":0},{"hour":21,"time":66},{"hour":22,"time":210},{"hour":23,"time":0}] ');
var w = 15,
h = 80;
var xScale = d3.scale.linear()
.domain([0, 1])
.range([0, w]);
var yScale = d3.scale.linear()
.domain([0, d3.max(data, function (d) {
return d.time;
})])
.rangeRound([5, h]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var chart = d3.select("#viz")
.append("svg")
.attr("class", "chart")
.attr("width", w * data.length - 1)
.attr("height", h);
chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) {
return xScale(i) - 0.5;
})
.attr("y", function(d) {
return h - yScale(d.time) - 0.5;
})
.attr("width", w)
.attr("height", function(d) {
return yScale(d.time);
});
chart.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
if (d.time > 10) {
return Math.round(d.time);
}
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "#FFF")
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + w / 2;
})
.attr("y", function(d) {
return h - yScale(d.time) - 0.5 + 10;
});
chart.append("g")
.attr("transform", "translate(0," + (h) + ")")
.call(xAxis);
function redraw() {
// This the part where the incoming data set also changes, which means the update to x-axis y-axis, labels
yScale.domain([0, d3.max(data, function (d) {
return d.time;
})]);
var bars = d3.selectAll("rect")
.data(data, function (d) {
return d.hour;
});
bars
.transition()
.duration(500)
.attr("x", w) // <-- Exit stage left
.remove();
d3.selectAll("rect") // This is actually empty
.data(data, function (d) {
return d.hour;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
console.log(d, d.day, xScale(d.day));
return xScale(d.day);
})
.attr("y", function(d) {
return yScale(d.time);
})
.attr("width", w)
.attr("height", function (d) {
return h - yScale(d.time);
});
}
d3.select("button").on("click", function() {
console.log('Clicked');
redraw();
});
Agree with Sam (although there were a few more issues, like using remove() without exit(), etc.) and I am putting this out because I was playing with it as I was cleaning the code and applying the update pattern. Here is the FIDDLE with changes in code I made. I only changed the first few data points but this should get you going.
var data2 = JSON.parse('[{"hour":0,"time":153},{"hour":1,"time":10},{"hour":2,"time":35},{"hour":3,"time":150},
UPDATE: per request, adding logic to consider an update with new data. UPDATED FIDDLE.
Since you're binding the same data to bars, the enter selection is empty. Once you remove the existing bars, you append a new bar for each data point in the enter selection - which again is empty. If you had different data, the bars should append.
If you haven't read through it already, the general update pattern is a great resource for understanding this sort of thing.

load map detail on click using D3.js

I am trying to build a map from the USA. Once the map is displayed with it's states boundaries I want to display the county boundaries on click
This is how I display the map:
var width = 960,
height = 500,
centered;
var path = d3.geo.path();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", loadcounties);
var g = svg.append("g")
.attr("id", "states");
d3.json("readme.json", function(json) {
g.selectAll("path")
.data(json.features)
.enter().append("path")
.attr("d", path)
.on("click", loadcounties);
});
And here is the function I use to load the regions (only alabama in here):
function loadcounties (d) {
var id = d3.select(this).attr("name");
var bbox = this.getBBox();
if (id == "Alabama") {
d3.json("alabamacounties.json", function(json) {
g.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path);
});
}
}
When I try to display Alabama on its own it works but as a function it doesn't. Any idea of what I am doing wrong?thanks
This is how I solved it:
function loadstate(d) {
var g = svg.append("g")
.attr("id", "counties");
switch (d.id){
case "01" :
jsoncounty="countiesjson/01.json";
break;
Like this for every state and then:
d3.json(jsoncounty, function(json) {
g.selectAll("path")
.data(json.features, function(d,i) { return d+i; })
.enter()
.append("path")
.attr("d", path)
.on("click",text)
.append("title")
.text(function(d) { return d.properties.NAME; });
});

Categories