Hello I am trying to build a multi-line chart that displays rates for each month year over year. I am getting a "TypeError: t is undefined" error in the console and while I can see the x and y axis populated, no lines are appearing in the chart. Any help appreciated!
The HTML file code is here:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.ordinal().range(["#666666", "#262626", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
var line = d3.svg.line().interpolate("basis").x(function(d) { return x(d.date); }).y(function(d) { return y(d.value); });
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 + ")");
d3.json("yoy_values.json", function(error, data) {
if (error) throw error;
var myValues = d3.keys(data[0]).filter(function(key) { return key !== "date"; });
data.forEach(function(d) {
d.rates = myValues.map(function(name) { return {name: name, value: +d[name]}; });
console.log(d.rates);
});
x.domain(data.map(function(d) { return d.date; }));
y.domain([
d3.min(data, function(d) { return d3.min(d.rates, function(d) { return d.value; }); }),
d3.max(data, function(d) { return d3.max(d.rates, function(d) { return d.value; }); })
]);
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("Rates");
var state = svg.selectAll(".state")
.data(myValues.slice().reverse())
.enter().append("g")
.attr("class", "state")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
state.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.rates); })
.style("stroke", function(d) { return color(d.name); });
state.append("text")
.datum(function(d) { return {name: d.name, value: d.rates[d.rates.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value) + "," + y(d.name) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});
</script>
The JSON file I am reading looks like this:
[
{"date":"Jan","2014":"0.0812","2015":"0.0780","2016":"0.0838"},
{"date":"Feb","2014":"0.0806","2015":"0.0768","2016":"0.0893"},
{"date":"Mar","2014":"0.0858","2015":"0.0847","2016":null},
{"date":"Apr","2014":"0.0848","2015":"0.0889","2016":null},
{"date":"May","2014":"0.0890","2015":"0.0890","2016":null},
{"date":"Jun","2014":"0.0928","2015":"0.0865","2016":null},
{"date":"Jul","2014":"0.0857","2015":"0.0799","2016":null},
{"date":"Aug","2014":"0.0905","2015":"0.0845","2016":null},
{"date":"Sep","2014":"0.1003","2015":"0.0934","2016":null},
{"date":"Oct","2014":"0.0971","2015":"0.0993","2016":null},
{"date":"Nov","2014":"0.0912","2015":"0.0973","2016":null},
{"date":"Dec","2014":"0.0800","2015":"0.0777","2016":null}
]
It's hard to tell from just code eyeballing, but I noticed:
You make a series of .rates properties from the non-"date" json data, so .rates for the first datum would be [{name:"2014", value:"0.0812"},{name:"2015", value:"0.0780"},{name:"2016", value:"0.0838"}]
d.rates = myValues.map(function(name) { return {name: name, value: +d[name]}; });
You then pass these to a d3.svg.line function
.attr("d", function(d) { return line(d.rates); })
But this line function has been previously set up to seemingly expect .date and .value properties not .name and .value
var line = d3.svg.line().interpolate("basis").x(function(d) { return x(d.date); }).y(function(d) { return y(d.value); });
And the scale x has been set up to expect .date (the month strings) as arguments, because they were set as the domain
x.domain(data.map(function(d) { return d.date; }));
In short, it seems the x scale and line function are expecting data objects categorised by month to draw one line per year, but you're passing in the .rates objects which are organised by year in the expectation of drawing one line per month?
Related
I'm trying to build a chart that goes from 1 line to many lines. I have built the two charts separately, but am having trouble combining them. The idea is that I want to show the total of all fruit sold per year, and then show how much of each fruit is sold per year.
This is the template I'm working from: http://bl.ocks.org/d3noob/a048edddbf83bff03a34
In my code, the single line shows up fine. When I click update, the axes update as they should, but the data doesn't. Any help would be greatly appreciated.
My code is in a plunker, here: https://plnkr.co/edit/dgwsGLIRbZ2qm7faEvSw?p=preview
The code is also below.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body { font: 12px Arial;}
path {
stroke: #333;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<div id="option">
<input name="updateButton" id="updateData" type="button" value="Update" />
<input name="revertButton" type="button" value="Revert" onclick="revertData()" />
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 800 - margin.left - margin.right,
height = 470 - margin.top - margin.bottom;
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom().scale(x)
.ticks(7)
.tickFormat(d3.format("d"))
var yAxis = d3.axisLeft().scale(y)
.ticks(5);
var valueline = d3.line()
.x(function(d) { return x(d.Year); })
.y(function(d) { return y(d.Count); });
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 + ")");
d3.csv("datab.csv", function(error, data2) {
d3.csv("dataa.csv", function(error, data) {
data.forEach(function(d) {
d.Year = +d.Year;
d.Count = +d.Count;
});
x.domain(d3.extent(data, function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.Count; })]);
dataNest1 = d3.nest()
.key(function(d) {return d.Type;})
.entries(data);
var result1 = dataNest1.filter(function(val,idx, arr){
return $("." + val.key)
})
var calls = d3.select("svg").selectAll(".line")
.data(result1, function(d){return d.key})
var color1 = d3.scaleOrdinal().range(["#333", "none", "none", "none", "none", "none"]);
calls.enter().append("path")
.attr("class", "line")
.attr("stroke","#333")
.attr("d", function(d){
return valueline(d.values)
})
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
d3.select('#updateData').on('click',function(){
updateData(data2)
})
});
});
function updateData(data2) {
data2.forEach(function(d) {
d.Year = +d.Year;
d.Count = +d.Count;
});
dataNest = d3.nest()
.key(function(d) {return d.Descriptor;})
.entries(data2);
var result = dataNest.filter(function(val,idx, arr){
return $("." + val.key)
})
x.domain(d3.extent(data2, function(d) { return d.Year; }));
y.domain([0, d3.max(data2, function(d) { return d.Count; })]);
var svg = d3.select("body").transition();
svg.selectAll('.circle').duration(0).remove()
d3.select("svg").selectAll(".line")
.data(result, function(d){return d.key})
d3.select("svg").selectAll("path.line")
.transition()
.duration(700)
.style("stroke", "#333")
.attr("d", function(d){
return valueline(d.values)
});
// svg.select(".line") // change the line
// .duration(750)
// .attr("d", valueline(rats));
svg.select(".x.axis")
.transition()
.duration(750)
.call(xAxis);
svg.select(".y.axis")
.duration(750)
.call(yAxis);
}
function revertData() {
// Get the data again
d3.csv("totalsbyyear.csv", function(error, data) {
data.forEach(function(d) {
d.Year = +d.Year;
d.Count = +d.Count;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.Count; })]);
// Select the section we want to apply our changes to
var svg = d3.select("body").transition();
// Make the changes
svg.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
svg.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
</script>
</body>
There are a few problems here. The first one being that your datasets are very different and I don't see anywhere in the code to compensate for there being multiple different fruit in the same year. This is something you will need to address either by modifying your csv file or in your updateData function to extract just a single fruit.
I have made some changes to your plunker that will allow the updateFunction to work but without fixing the input data it won't really matter.
https://plnkr.co/edit/DSg09NWPrBADLLbcL5cx?p=preview
The main thing that needed to be fixed was
d3.select("svg").selectAll("path.line")
.data(result, function(d){return d.key})
.transition()
.duration(700)
.style("stroke", "#337")
.attr("d", function(d){
return valueline(result)
});
And putting the d3.csv on datab.csv call in the updateData function instead of wrapping it around the main table creation function.
I was probably being unclear in my question asking. I figured out what I needed to do to make it work as intended. Code here:
https://plnkr.co/edit/YrABkbc8l9oeT1ywMspy?p=preview
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body { font: 12px Arial;}
path {
stroke: #333;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<div id="option">
<input name="updateButton" id="updateData" type="button" value="Update" />
<input name="revertButton" type="button" value="Revert" onclick="revertData()" />
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<script>
var margin = {top: 30, right: 200, bottom: 30, left: 50},
width = 850 - margin.left - margin.right,
height = 470 - margin.top - margin.bottom;
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom().scale(x)
.ticks(7)
.tickFormat(d3.format("d"))
var yAxis = d3.axisLeft().scale(y)
.ticks(5);
var valueline = d3.line()
.x(function(d) { return x(d.Year); })
.y(function(d) { return y(d.Count); });
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 + ")");
d3.csv("datab.csv", function(error, data2) {
d3.csv("dataa.csv", function(error, data) {
data.forEach(function(d) {
d.Year = +d.Year;
d.Count = +d.Count;
});
x.domain(d3.extent(data, function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.Count; })]);
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
svg.append("text")
.attr("transform", function(d) { return "translate(" + x(2017) + "," + y(35074) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.attr('class','toplinetext')
.style("font", "10px sans-serif")
.text(function(d) { return "All"; });
circles = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr('class','circle')
.attr('cx',function(d){ return x(d.Year)})
.attr('cy',function(d){ return y(d.Count)})
.attr('r',3)
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.exit().remove();
d3.select('#updateData').on('click',function(){
updateData(data2)
})
});
});
function updateData(data2) {
data2.forEach(function(d) {
d.Year = +d.Year;
d.Count = +d.Count;
});
var notapples = data2.filter(function(d){return d.Descriptor == "Peach" || d.Descriptor == "Pear" || d.Descriptor == "Plum" || d.Descriptor == "Banana"})
dataNest = d3.nest()
.key(function(d) {return d.Descriptor;})
.entries(notapples);
var result = dataNest.filter(function(val,idx, arr){
return $("." + val.key)
})
$('.toplinetext').text('Apples');
x.domain(d3.extent(data2, function(d) { return d.Year; }));
y.domain([0, d3.max(data2, function(d) { return d.Count; })]);
var svg = d3.select("body").transition();
svg.selectAll('.circle').duration(0).remove()
var typeOfCall = d3.select("svg").selectAll(".dline")
.data(result, function(d){return d.key})
.enter().append("g")
.attr("class", function(d){return "type " + d.key})
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var color2 = d3.scaleOrdinal().range(["#ff0000", "#333", "#333", "#333", "#333", "#333"]);
typeOfCall.append("path")
.attr("height", 0)
.transition()
.duration(700)
.attr("class", "line")
.style("stroke", function(d,i) { return color2(d.key); })
.attr("d", function(d){
return valueline(d.values)
});
typeOfCall.append("text")
.datum(function(d) { return {id: d.Descriptor, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.Year) + "," + y(d.value.Count) + ")"; })
.attr("x", 3)
.attr("dy", "0.35em")
.style("font", "10px sans-serif")
.text(function(d) { return d.value.Descriptor; });
typeOfCall.exit().remove();
apples = data2.filter(function(d){return d.Descriptor == "Apple"})
svg.select(".line")
.duration(750)
.attr("d", valueline(apples));
svg.select(".x.axis")
.transition()
.duration(750)
.call(xAxis);
svg.select(".y.axis")
.duration(750)
.call(yAxis);
}
function revertData() {
// Get the data again
d3.csv("totalsbyyear.csv", function(error, data) {
data.forEach(function(d) {
d.Year = +d.Year;
d.Count = +d.Count;
});
$('.toplinetext').text('All');
$('.Peach,.Pear,.Banana,.Plum').remove();
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.Count; })]);
// Select the section we want to apply our changes to
var svg = d3.select("body").transition();
// Make the changes
svg.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
svg.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
</script>
</body>
i got a normal line chart written in d3.js
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis {
position: fixed;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 2000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.temperature); });
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 + ")");
d3.tsv("data.tsv", function(error, data) {
if (error) throw error;
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(cities, function(c) { return d3.min(c.values, function(v) { return v.temperature; }); }),
d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); })
]);
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("Temperature (ยบF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
cities.forEach(function(d) {
console.log(d);
});
city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
city.append("text")
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});
but the height is higher than the screen size, so i need to scroll
But now i need to see the Axes all the time.
So i need the x-Axis and the y-Axis with fixed position
I tried CSS position:fixed but it had no effect.
Please help me.
Try applying the style when you generate the axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.style("position", "fixed")
.call(xAxis);
window.onscroll = function() {myFunction()};
function myFunction() {
if(window.pageYOffset == 0)
{
d3.select("#test").nodes()[0].setAttribute("transform", "translate(200, " + 10 +")")
}
if (window.pageYOffset > 0) {
d3.select("#test").nodes()[0].setAttribute("transform", "translate(200, " + window.pageYOffset +")")
}
}
UPDATED
Problem
a) Only one of my bar charts is showing after I switched a variable var csvData in my scripts.js to the real data from the dummy data. The previous data referenced states and demographics, which has now been switched to food and their prices.
scripts.js (UPDATED, chart still not showing up)
$(function() {
$("#placeholder").remove();
var margin = {top: 60, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// X is the horizontal axis
var x0 = d3.scale.ordinal() // ordinal for non quantitative scales, like names, categories
.rangeRoundBands([0, width], .1); // Width of each individual bar?
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
// Bar chart colors
var color = d3.scale.ordinal()
.range(["#001F4C", "#003D99", "#005CE6", "#0066FF", "#3385FF", "#80B2FF", "#CCE0FF", "#E6F0FF", "#E6EBFA"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left") // Where the Y axis goes, you'll want it on the left
.ticks(8)
.tickValues([0, 1, 2, 3, 4, 5, 6]);
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 + ")");
// Bar chart data
var csvData = [
{"Store":"Co-op","Broccoli":2.69,"Cauliflower":3.69,"Celery":1.89,"Strawberries":4.49,"Oranges":1.69,"Tomatoes":3.49,"Lemons":0.99, "Lettuce":0.01, "Cucumber":2},
{"Store":"Safeway","Broccoli":2.97,"Cauliflower":3.98,"Celery":1.77,"Strawberries":5.96,"Oranges":0.97,"Tomatoes":2.97,"Lemons":0.77, "Lettuce":4.97, "Cucumber":1.97},
{"Store":"Sobeys","Broccoli":3.49,"Cauliflower":3.99,"Celery":1.29,"Strawberries":3.99,"Oranges":1.99,"Tomatoes":4.99,"Lemons":1.29, "Lettuce":3.49, "Cucumber":1.99},
{"Store":"Superstore","Broccoli":2.69,"Cauliflower":2.49,"Celery":1.09,"Strawberries":2.99,"Oranges":0.99,"Tomatoes":3.99,"Lemons":0.99, "Lettuce":4.99, "Cucumber":2.49},
];
var data = csvData;
var foodNames = d3.keys(data[0]).filter(function(key) { return key !== "Store"; });
data.forEach(function(d) {
d.food = foodNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Store; }));
x1.domain(foodNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.food, function(d) { return d.value; }); })]);
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)") // Rotates the label on the Y axis
.attr("y", 8) // Label spacing from Y axis
.attr("dy", ".71em") // Label spacing from Y axis
.style("text-anchor", "end") // Anchor the label to the end of the Y axis
.text("Price (in dollars)"); // Changes the text on the Y or vertical axis
var store = svg.selectAll(".store") // Selects all of the data in column labelled store
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.store) + ",0)"; });
store.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("class", "stick")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 22.5 + ")"; }); // Determines spacing between items in legend
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="assets/js/scripts.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
</body>
</html>
style.css
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
/*.stick {
fill: steelblue;
}
.stick:hover {
fill: brown;
}*/
.x.axis path {
display: none;
}
$(function() {
$("#placeholder").remove();
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// X is the horizontal axis
var x0 = d3.scale.ordinal() // ordinal for non quantitative scales, like names, categories
.rangeRoundBands([0, width], .1); // Width of each individual bar?
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0])
// These are the colors
var color = d3.scale.ordinal()
// .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
.range(["#001F4C", "#003D99", "#005CE6", "#0066FF", "#3385FF", "#80B2FF", "#CCE0FF", "#E6F0FF"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left") // Where the Y axis goes, you'll want it on the left
// .tickFormat(d3.format(".1s"));
.ticks(6);
//.tickValues([0, 1, 2, 3, 4, 5]);
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 + ")");
// Bar chart data
var csvData = [
{"State":"Co-op","Broccoli":2027307,"Cauliflower":3277946,"Celery":1420518,"Strawberries":2454721,"Oranges":7017731,"Tomatoes":5656528,"Lemons":2472223, "Lettuce":2472223, "Cucumber":2472223},
{"State":"Safeway","Broccoli":2704659,"Cauliflower":4499890,"Celery":2159981,"Strawberries":3853788,"Oranges":10604510,"Tomatoes":8819342,"Lemons":4114496, "Lettuce":2472223, "Cucumber":2472223},
{"State":"Sobeys","Broccoli":1140516,"Cauliflower":1938695,"Celery":925060,"Strawberries":1607297,"Oranges":4782119,"Tomatoes":4746856,"Lemons":3187797, "Lettuce":2472223, "Cucumber":2472223},
{"State":"Superstore","Broccoli":1208495,"Cauliflower":2141490,"Celery":1058031,"Strawberries":1999120,"Oranges":5355235,"Tomatoes":5120254,"Lemons":2607672, "Lettuce":2472223, "Cucumber":2472223},
];
// Food Prices
// var csvData = [
// {"Store":"Sobey","Broccoli":2.69,"Cauliflower":3.69,"Celery":$1.89,"Strawberries":$4.49,"Oranges":"1.69,"Tomatoes":$3.49,"Lemons":$0.99,"Lettuce":$0.00,"Cucumber":2.00},
// {"Store":"Superstore","Broccoli":2.97,"Cauliflower":3.98,"Celery":1.77,"Strawberries":5.96,"Oranges":0.97,"Tomatoes":2.97,"Lemons":0.77,"Lettuce":4.97,"Cucumber":1.97},
// {"Store":"Safeway","Broccoli":3.49,"Cauliflower":3.99,"Celery":1.29,"Strawberries":3.99,"Oranges":1.99,"Tomatoes":4.99,"Lemons":1.29,"Lettuce":3.49,"Cucumber":1.99},
// {"Store":"Coop","Broccoli":2.69,"Cauliflower":"2.49","Celery":"1.09","Strawberries":"2.99","Oranges":"0.99","Tomatoes":"3.99","Lemons":"0.99","Lettuce":"4.99","Cucumber":"2.49"}
// ];
// AgeNames = FoodNames
// States = Stores
var data = csvData;
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: (+d[name])/2000000}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.ages, function(c) {
return c.value;
});
})]);
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)") // Rotates the label on the Y axis
.attr("y", 8) // Label spacing from Y axis
.attr("dy", ".71em") // Label spacing from Y axis
.style("text-anchor", "end") // Anchor the label to the end of the Y axis
.text("Price (in dollars)"); // Changes the text on the Y or vertical axis
var state = svg.selectAll(".state") // Selects all of the data in column labelled State
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("class", "stick")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 22.5 + ")"; });
// Number (20) determines spacing between items in legend
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
/*.stick {
fill: steelblue;
}
.stick:hover {
fill: brown;
}*/
.x.axis path {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
To achieve our requirement I've added one thing,i.e.
(+d[name])/2000000 while assigning the value I'm just dividing it to fit to our scale. y domain is set to very big number like near to one Crore.
I want to display the bar charts each bar peak value at the outer top of each bar. But as far as i could get is to represent each bar value inside the bar. But thats not what i wanted. As you can see in the image attached the bottom of the x axis is messed up. I think we have to move the svg code out of the data loop and use svg.data(someData).enter() to fix this issue but i tried ways to do it but unsuccessful. Kindly suggest how i can modify the follow code.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.chart text {
fill: black;
font: 15px sans-serif;
text-anchor: middle;
}
</style>
<svg class="chart"></svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 80, bottom: 40, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var color = d3.scale.category10();
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .2);
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");
function unit (d) {
count = 0, download = 0;
num = d.value;
while((num) >= 1)
{
num/=10;
++count;
}
switch (count) {
case 4 : download = ((d.value/1000).toFixed(2)) + " MB" ;
break;
case 8 : download = ((d.value/1000 * 1000).toFixed(2)) + " GB" ;
break;
default: download = ((d.value/1000).toFixed(2)) + " MB" ;
}
return download;
}
var chart = d3.select("body").append("svg")
// line = 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 + ")");
var bar;
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 790 )
.attr("y", 15 )
.style("text-anchor", "bottom")
.text("Clients");
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
//.attr("transform", "rotate(-90)")
.attr("y", 1)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Kilo Bytes");
bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
bar.append("rect")
.style("fill", function(d) { return color(d.name); })
.attr("y", function(d) { return y(d.value);})
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.rangeBand());
bar.append("text")
.attr("x", x.rangeBand() / 2 )
.attr("y", function(d) { return y(d.value) + 2 ; })
.attr("dy", "1em")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.text(function(d) { return unit(d); });
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
and the data is in the csv format as below:
name,timesatmp,value
98.226.148.140,1341677167454,1
98.226.148.140,1341677167461,3
98.226.148.141,1341677167916,4
98.226.148.141,1341677167935,6
98.226.148.142,1341677167944,7
98.226.148.142,1341677167945,32
Kindly suggest how i can show the display properly.
First modify your bar charts a little, otherwise no bars will be shown there:
html:
<svg class="chart"></svg>
to
<body class="chart"></body>
js:
bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr('class', 'bar')
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
and for adjusting the peak value, just add a translate as follows:
bar.append("text")
.attr("x", x.rangeBand() / 2 )
.attr("y", function(d) { return y(d.value) + 2 ; })
.attr("dy", "1em")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.attr("transform", function(d) { return "translate(0, -20)"; })
.text(function(d) { return unit(d); });
Rplace:
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
with:
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var nestedData = d3.nest()
.key(function(d) { return d.name; }).entries(data);
var newData = [];
nestedData.forEach(function(d){
newData.push({name:d.key, value: d3.max(d.values, function(d) {return d.value})});
});
I've got an update button working that loads new .csv data into a D3.s stacked bar chart. My problem is that on update it seems to be using both data sets instead of just the new one. I think this is related to some confusion I have about update and selectAll, but I haven't been able to figure out how to replace instead of append. Here's my current code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<!-- <label><input type="checkbox"> Sort values</label> -->
<div id="option">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()" />
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 200, left: 40},
width = 960 - margin.left - margin.right,
height = 650 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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 + ")");
d3.csv("FinalData4.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
//data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
// x-axis label
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
// y-axis label
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("USD in Millions");
// adds bars
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
// set up the legend
var legend = svg.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
// ** Update data section (Called from the onclick)
function updateData() {
// Get the data again
d3.csv("FinalData2015.csv", function(error, data2) {
color.domain(d3.keys(data2[0]).filter(function(key) { return key !== "State"; }));
data2.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
x.domain(data2.map(function(d) { return d.State; }));
y.domain([0, d3.max(data2, function(d) { return d.total; })]);
var tran = d3.select("body").transition();
// change x-axis label
tran.select(".x.axis")
.duration(1000)
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
// change the y-axis label
tran.select(".y.axis")
.duration(1000)
.call(yAxis);
// adds bars
var state = svg.selectAll(".State")
.data(data2)
.enter().append("g")
//.transition()
//.duration(1000)
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
var test = state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.transition()
.duration(1000)
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
});
}
</script>
</body>
UPDATE: I changed the functionUpdateData() code to what I have currently because I realized it had some junk in there. New code has working transitions for x and y axis labels, but still the same problem described above with the actual bars.
It is because you are not using update and exiting joins in the update function (read this article carefully http://bost.ocks.org/mike/join/). As far I understand, you want to update the existing csv data with the new one, for that you need to update the current set of data. I'll give you an example with a simpler code:
function updateData() {
//this is the new data that you are binding to some circles on the canvas
var circle = svg.selectAll("circle").data([200, 100, 50,10,5]);
//if there is new data, you get those new circles with the enter() method
var circleEnter = circle.enter().append("circle");
circleEnter.attr("cy", 60);
circleEnter.attr("cx", function(d, i) { return i * 100 + 30; });
circleEnter.attr("r", function(d) { return Math.sqrt(d); });
//------THIS IS THE PART YOU ARE MISSING------
//but the circles that already exist need an update. (check there is no enter() method here)
circle.attr("r", function(d) { return Math.sqrt(d); });
//also you need to remove the circles that don´t have a data binding.
circle.exit().remove();
}
so I think that in your code you have to write something like:
var state = svg.selectAll(".state").data(data);
//the update data
state.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });