I am creating an interactive bar chart in D3. When a button is pressed the data changes.
Here's simplified code for the bar chart:
<svg class="cex"></svg>
<script>
var margin = {top: 20, right: 20, bottom: 60, left: 35},
width = 650 - margin.left - margin.right,
height = 300- margin.top - margin.bottom;
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)
.outerTickSize(0)
.orient("left");
var cex = d3.select(".cex")
.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("/input.csv", function(error, data) {
x.domain(data.map(function(d) { return d.City; }));
y.domain([0, d3.max(data, function(d) { return d.EatIn; })]);
cex.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("y", 0)
.attr("x", 9)
.attr("dy", ".35em")
.attr("transform", "rotate(60)")
.style("text-anchor", "start");;
cex.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar=cex.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.City); })
.attr("y", function(d) { return y(d.EatIn); })
.attr("height", function(d) { return height - y(d.EatIn); })
.attr("width", x.rangeBand());
});
function type(d) {
d.EatIn = +d.EatIn;
return d;
}
When a button is selected the following update code runs:
function EatOutData() {
d3.csv("/input.csv", function(error, data) {
x.domain(data.map(function(d) { return d.City; }));
y.domain([0, d3.max(data, function(d) { return d.EatOut; })]);
var sel = cex.selectAll("rect")
.data(data);
sel.exit().remove();
sel.enter().append("rect")
sel.attr("class", "bar")
sel.attr("x", function(d) { return x(d.City); })
sel.attr("y", function(d) { return y(d.EatOut); })
sel.attr("height", function(d) { return height - y(d.EatOut); })
sel.attr("width", x.rangeBand());
sel.selectAll("g.y.axis")
.call(yAxis);
sel.selectAll("g.x.axis")
.call(xAxis);
function type(d) {
d.EatOut = +d.EatOut;
return d;
}
}
)};
The update changes the Y variable. So the height of the bar changes but the axis doesn't change and the scale of the two variables are quite different.
There have been a couple other SO posts on this but none seemed to fix it for me. I'm not sure why the y.domain in the update doesn't adjust them. Would really appreciate any suggestions.
You will have to remove the axis (or the text) and draw it again just like you are removing the bars and plotting them again to update data on the axis. Check the working plnkr here.
function EatOutData() {
d3.csv("input2.csv", function(error, data) {
x.domain(data.map(function(d) { console.log(d); return d.City; }));
y.domain([0, d3.max(data, function(d) { return d.EatOut; })]);
var sel = cex.selectAll("rect")
.data(data);
sel.exit().remove();
d3.select(".x.axis").remove();
sel.enter().append("rect")
sel.attr("class", "bar")
sel.attr("x", function(d) { return x(d.City); })
sel.attr("y", function(d) { return y(d.EatOut); })
sel.attr("height", function(d) { return height - y(d.EatOut); })
sel.attr("width", x.rangeBand());
cex.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("y", 0)
.attr("x", 9)
.attr("dy", ".35em")
.attr("transform", "rotate(60)")
.style("text-anchor", "start");;
sel.selectAll("g.y.axis")
.call(yAxis);
sel.selectAll("g.x.axis")
.call(xAxis);
function type(d) {
d.EatOut = +d.EatOut;
return d;
}
}
)};
Related
I have a set of nested json data:
var data = [{"time":"2016-03-01","values":[{"specimen_count":1,"trap":"S0024", "species":1},{"specimen_count":2,"trap":"S0025", "species":2},{"specimen_count":2,"trap":"S0026", "species":2}]},{"time":"2016-03-15","values":[{"specimen_count":6,"trap":"S0024", "species":6},{"specimen_count":5,"trap":"S0025", "species":4},{"specimen_count":7,"trap":"S0026", "species":6}]}];
And I want to draw a set of grouped bar charts each group representing a time interval and each group with 3 bars, each representing a trap, and the height of the bar is the specimen_count field.
Now I want to add a scatterplot, one dot for each bar and the height of the dot is the species field, using the same scales. But I am having trouble successfully placing the dots on top the the grouped bar chart. I did manage to add a line with the species data, but I can't add the dots using the same logic.
Here is my code:
var margin = {top: 100, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#ca0020","#f4a582","#92c5de"]);
var svg = d3.select('#chart').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 categoriesNames = data.map(function(d) { return d.time; }); // the 5 time periods
var trapNames = data[0].values.map(function(d) { return d.trap; }); // the name of the traps
console.log(trapNames);
x0.domain(categoriesNames);
x1.domain(trapNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(category) { return d3.max(category.values, function(d) { return d.specimen_count; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style('opacity','0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.text("Value");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "slice")
.attr("transform",function(d) { return "translate(" + x0(d.time) + ",0)"; });
slice.selectAll("rect")
.data(function(d) { return d.values; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.trap); })
.style("fill", function(d) { return color(d.trap) })
.attr("y", function(d) { return y(0); })
.attr("height", function(d) { return height - y(0); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.trap)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.trap));
});
slice.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
.attr("y", function(d) { return y(d.specimen_count); })
.attr("height", function(d) { return height - y(d.specimen_count); });
var valueline = d3.svg.line()
.x(function (d) { return x1(d.trap) + x1.rangeBand()/2; })
.y(function (d) { return y(d.species); });
slice.enter()
.append('path')
.attr('class','line')
.style('stroke', "#0571b0")
.style('stroke-width', "3px")
.attr('fill', 'none')
.attr('d', function(d) { return valueline(d.values); });
slice.selectAll('.dot').data(data,function(d){return d.time;})
.enter()
.append("circle")
.attr("class", "dot")
.attr("r",5)
.attr("cx", function(d){
return x1(d.trap) + x1.rangeBand()/2;
})
.attr("cy",function(d){
return y(d.species);
})
.attr("fill","#0571b0");
There error I'm getting from the circle-related code is: d3.min.js:1 Error: attribute cx: Expected length, "NaN".
I think the nested data and the ordinal scale for the bar chart is throwing me off a bit, so it could be that I am not understanding fulling data access in these cases.
Also here is the screenshot of the current graph
If you need the dots on every bar chart, then the data() callback must return a list of bars not a single item. Did you try replacing it with:
slice.selectAll('.dot')
.data(function(d) {
return d.values;
})
.enter()
.append("circle") //... and so on
Doing this will use the existing data object (with 5 bar groups), but render a dot for each bar.
Here it is running:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
var data = [{
"time": "2016-03-01",
"values": [{
"specimen_count": 1,
"trap": "S0024",
"species": 1
}, {
"specimen_count": 2,
"trap": "S0025",
"species": 2
}, {
"specimen_count": 2,
"trap": "S0026",
"species": 2
}]
}, {
"time": "2016-03-15",
"values": [{
"specimen_count": 6,
"trap": "S0024",
"species": 6
}, {
"specimen_count": 5,
"trap": "S0025",
"species": 4
}, {
"specimen_count": 7,
"trap": "S0026",
"species": 6
}]
}];
var margin = {
top: 100,
right: 20,
bottom: 30,
left: 40
},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#ca0020", "#f4a582", "#92c5de"]);
var svg = d3.select('#chart').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 categoriesNames = data.map(function(d) {
return d.time;
}); // the 5 time periods
var trapNames = data[0].values.map(function(d) {
return d.trap;
}); // the name of the traps
console.log(trapNames);
x0.domain(categoriesNames);
x1.domain(trapNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(category) {
return d3.max(category.values, function(d) {
return d.specimen_count;
});
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style('opacity', '0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight', 'bold')
.text("Value");
svg.select('.y').transition().duration(500).delay(1300).style('opacity', '1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "slice")
.attr("transform", function(d) {
return "translate(" + x0(d.time) + ",0)";
});
slice.selectAll("rect")
.data(function(d) {
return d.values;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.trap);
})
.style("fill", function(d) {
return color(d.trap)
})
.attr("y", function(d) {
return y(0);
})
.attr("height", function(d) {
return height - y(0);
})
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.trap)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.trap));
});
slice.selectAll("rect")
.transition()
.delay(function(d) {
return Math.random() * 1000;
})
.duration(1000)
.attr("y", function(d) {
return y(d.specimen_count);
})
.attr("height", function(d) {
return height - y(d.specimen_count);
});
var valueline = d3.svg.line()
.x(function(d) {
return x1(d.trap) + x1.rangeBand() / 2;
})
.y(function(d) {
return y(d.species);
});
slice
.append('path')
.attr('class', 'line')
.style('stroke', "#0571b0")
.style('stroke-width', "3px")
.attr('fill', 'none')
.attr('d', function(d) {
return valueline(d.values);
});
slice.selectAll('.dot').data(function(d) {
return d.values;
})
.enter()
.append("circle")
.attr("class", "dot")
.attr("r", 5)
.attr("cx", function(d) {
return x1(d.trap) + x1.rangeBand() / 2;
})
.attr("cy", function(d) {
return y(d.species);
})
.attr("fill", "#0571b0");
</script>
</body>
</html>
really stucked on this issue for days. I am trying to update my d3 graph to show how long it takes to run a function on a calculator. With the info I get, I will show the time taken and display it on the graph that I previously drew. My issue here now is that I just can't get the graph to update.
Codes for the graph:
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width])
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");
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
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 + ")");
var data = dataone.map(function(d) {
return {
date:d[0],
close: d[1]
};
});
console.log(data);
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
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("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
Code for update function and update of array:
function updateArray(){
dataone.push(clickss-0.5,clickss);
updateData();
}
function updateData() {
var data = dataone.map(function(d) {
return {
date:d[0],
close: d[1]
};
});
// 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("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);
}
Please advise on how I can get this to work. I read quite a few guides now but
Couple things:
1.) In your update function, your line generator function is line not valueline.
2.) By looking at your accessor functions, I don't think you meant dataone.push(clickss-0.5,clickss); but rather dataone.push([clickss-0.5,clickss]);. You need an array of arrays.
In addition, you don't need to map your dataone array to another structure. Just change your accessors:
x.domain(d3.extent(data, function(d) {
return d[0];
}));
y.domain(d3.extent(data, function(d) {
return d[1];
}));
...
var line = d3.svg.line()
.x(function(d) {
return x(d[0]);
})
.y(function(d) {
return y(d[1]);
});
Here's your code working and cleaned up a bit:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var dataone = [];
for (var i = 0; i < 10; i++){
dataone.push([i,Math.random()]);
}
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width])
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");
var line = d3.svg.line()
.x(function(d) {
return x(d[0]);
})
.y(function(d) {
return y(d[1]);
});
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 + ")");
// Scale the range of the data again
x.domain(d3.extent(dataone, function(d) {
return d[0];
}));
y.domain([0, d3.max(dataone, function(d) {
return d[1];
})]);
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("Price ($)");
svg.append("path")
.datum(dataone)
.attr("class", "line")
.attr("d", line)
.style("fill","none")
.style("stroke","steelblue");
function updateData() {
// Scale the range of the data again
x.domain(d3.extent(dataone, function(d) {
return d[0];
}));
y.domain([0, d3.max(dataone, function(d) {
return d[1];
})]);
// 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", line(dataone));
svg.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
}
setInterval(function(){
for (var i = 0; i < 5; i++){
dataone.push([dataone.length + i, Math.random()]);
}
updateData();
},1000);
</script>
</body>
</html>
I have successfully managed to create a grouped bar chart that displays the various performance stats for soccer players over the course of one season. This data is all loaded from a csv file. I would now like to dynamically change that data whereby when a button is pressed, a new csv file is loaded with the stats corresponding to the next soccer season. I've tried to do this on my own but everytime I try reload the data, the old data as well as the old axis's remain present and the enw data loads ontop of these. It all ends up looking messy. So how do I have the old bars and axises updated in line with the new data? Here is my code:
$(document).ready(function(){
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.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("SoccerStatsCSV.csv", function(error, data) {
console.log(data);
var playerNames = d3.keys(data[0]).filter(function(key) { return key !== "Attribute"; });
console.log(playerNames);
data.forEach(function(d) {
d.Playerstats = playerNames.map(function(name) { return {name: name, value: +d[name]}; });
console.log(d.Playerstats);
});
x0.domain(data.map(function(d) { return d.Attribute; }));
x1.domain(playerNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.Playerstats, 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("Units");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Attribute) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.Playerstats; })
.enter().append("rect")
.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(playerNames.slice())
.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; });
});
//The updating button
d3.select("button")
.on('click',function(){
d3.csv("SoccerStatsCSV2008.csv", function(error, data) {
console.log(data);
var playerNames = d3.keys(data[0]).filter(function(key) { return key !== "Attribute"; });
console.log(playerNames);
data.forEach(function(d) {
d.Playerstats = playerNames.map(function(name) { return {name: name, value: +d[name]}; });
console.log(d.Playerstats);
});
x0.domain(data.map(function(d) { return d.Attribute; }));
x1.domain(playerNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.Playerstats, 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("Units");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Attribute) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.Playerstats; })
.enter().append("rect")
.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(playerNames.slice())
.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; });
});
});
});
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
i'm using the following code for generating multi line graph. I'm getting the result. But the graph looks like:
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 1025 - margin.left - margin.right,
height = 339 - margin.top - margin.bottom;
var x = d3.scale.linear()
.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.days); })
.y(function(d) { return y(d.testRuns); });
var svg = d3.select("#application_status_graph").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("js/lineChartData.json", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) {return key !== "days"; }));
data.forEach(function(d) {
d.days = parseInt(d.days);
});
var status = color.domain().map(function(name){
return{
name: name,
values: data.map(function(d){
return {days: d.days, testRuns: +d[name]};
})
}
});
x.domain(d3.extent(data, function(d) { return d.days; }));
y.domain([
d3.min(status, function(c) { return d3.min(c.values, function(v) { return v.testRuns; }); }),
d3.max(status, function(c) { return d3.max(c.values, function(v) { return v.testRuns; }); })
]);
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("Number of Test Runs");
var city = svg.selectAll(".city")
.data(status)
.enter().append("g")
.attr("class", "city");
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.days) + "," + y(d.value.testRuns) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});
But i need it in this form:
I'm new to d3. The attribute which i'm using is line only for both. But it is coming as curves. Any help is appreciated. Thanks
the problem is in var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.days); })
.y(function(d) { return y(d.testRuns); });
it should be changed to .interpolate("linear") to get the desired effect.
the wiki link explanes all types of line you can obtain:
https://github.com/mbostock/d3/wiki/SVG-Shapes#wiki-line_interpolate
hope this helps!