I'm trying to get 2 completely different d3 charts (2 line charts but totally different data - one with several lines and negative data, other with one line positive data) on the same page.
Right now, I only get the first one to be generated and shown correctly on the HTML page, the second chart doesn't show at all (not even svg container is generated).
Here is my code:
(function() {
// Get the data
d3.json("../assets/js/json/temperature.json", function(data) {
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 25},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.temps); })
.y(function(d) { return y(d.temperature); });
// prepare data
data.forEach(function(d) {
d.temps = parseDate(d.temps);
d.temperature = +d.temperature;
});
// Adds the svg canvas
var svg = d3.select("#graphTemp")
.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 on domain
x.domain(d3.extent(data, function(d) { return d.temps; }));
y.domain([0, d3.max(data, function(d) { return d.temperature; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
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("Temperatures");
});
})();
(function(){
// loads the data and loads it into chart - main function
d3.json("../assets/js/json/maitrise.json", function(data) {
var m = {top: 20, right: 5, bottom: 30, left: 40},
w = 70 - m.left - m.right,
h = 30 - m.top - m.bottom;
var x = d3.scale.linear().domain([0, data.length]).range([0 + m.left, w - m.right]);
var y = d3.scale.linear()
.rangeRound([h, 0]);
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d,i) { return x(i); })
.y(function (d) { return y(d.value); });
var color = d3.scale.ordinal()
.range(["#28c6af","#ffd837","#e6443c","#9c8305","#d3c47c"]);
var svg2 = d3.select("#maitrisee").append("svg")
.attr("width", w + m.left + m.right)
.attr("height", h + m.top + m.bottom)
.append("g")
.attr("transform", "translate(" + m.left + "," + m.top + ")");
// prep axis variables
var xAxis2 = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis2 = d3.svg.axis()
.scale(y)
.orient("left");
//console.log("Inital Data", data);
var labelVar = 'id'; //A
var varNames = d3.keys(data[0])
.filter(function (key) { return key !== labelVar;}); //B
color.domain(varNames); //C
var seriesData = varNames.map(function (name) { //D
return {
name: name,
values: data.map(function (d) {
return {name: name, label: d[labelVar], value: +d[name]};
})
};
});
console.log("seriesData", seriesData);
y.domain([
d3.min(seriesData, function (c) {
return d3.min(c.values, function (d) { return d.value; });
}),
d3.max(seriesData, function (c) {
return d3.max(c.values, function (d) { return d.value; });
})
]);
var series = svg2.selectAll(".series")
.data(seriesData)
.enter().append("g")
.attr("class", function (d) { return d.name; });
series.append("path")
.attr("class", "line")
.attr("d", function (d) { return line(d.values); })
.style("stroke", function (d) { return color(d.name); })
.style("stroke-width", "2px")
.style("fill", "none");
});
})();
OK, I found where the error was coming from. There was a piece of javascript in the middle of the HTML page that stopped d3 to generate the second graph further down in the page.
Thanks for all the help!
Related
Hey guys I created a time series line chart using publicly available stock data.
Where I got to is the following:
Looks like what it is doing is connecting the first datapoint with the last datapoint which is why it is creating a line across the entire chart.
I looked online and read that to create a non continuous line chart I can add
.defined(function(d) { return d; })
I did but it didn't help.
My code:
//Set dimensions and margins for the graph
var margin = {top: 20, right: 20, bottom: 100, left: 70},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//Create canvas
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 + ")");
//Parse date
var parseDate = d3.timeParse("%Y-%m-%d");
//Set the ranges
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
// Define the line
var valueLine = d3.line()
.defined(function(d) { return d; })
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
//Import data from api
d3.json("api_all.php", function(error, data) {
if (error) throw error;
data.forEach(e => {
e.date = parseDate(e.date);
e.value = +e.close;
e.stockName = e.stock_name;
e.stockSymbol = e.stock_symbol;
});
//Create nest variable
var nest = d3.nest()
.key(function(d){ return d.stockSymbol; })
.entries(data);
console.log(nest);
//Scale the range of the data
//x axis scale for entire dataset
x.domain(d3.extent(data, function(d) { return d.date; }));
//y.domain([0, d3.max(data, function(d) { return d.value; })]);
//Add the x axis
var xaxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.attr("class", "x axis")
.call(d3.axisBottom(x));
//Add x axis label
svg.append("text")
.attr("transform", "translate(" + (width/2) + "," + (height + margin.top + 10) + ")")
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Date")
.attr("class", "x axis label");
//Create dropdown
var dropDown = d3.select("#dropDown")
dropDown
.append("select")
.selectAll("option")
.data(nest)
.enter()
.append("option")
.attr("value", function(d){ return d.key; })
.text(function(d){ return d.key; })
// Function to create the initial graph
var initialGraph = function(stock){
// Filter the data to include only stock of interest
var selectStock = nest.filter(function(d){
return d.key == stock;
})
console.log(selectStock)
//Unnest selectStock for y axis
var unnested = function(data, children){
var out = [];
data.forEach(function(d, i){
console.log(i, d);
d_keys = Object.keys(d);
console.log(i, d_keys)
values = d[children];
values.forEach(function(v){
d_keys.forEach(function(k){
if (k != children) { v[k] = d[k]}
})
out.push(v);
})
})
return out;
}
var selectStockUnnested = unnested(selectStock, "values");
//Scale y axis
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock, function(d){
return d ? d.key : this.key;
})
.enter()
.append("g")
.attr("class", "stockGroups")
.each(function(d){
y.domain([0, d3.max(selectStockUnnested, function(d) { return d.value; })])
console.log(selectStockUnnested);
});
var initialPath = selectStockGroups.selectAll(".line")
.data(selectStock)
.enter()
.append("path")
initialPath
.attr("d", function(d){ return valueLine(d.values) })
.attr("class", "line")
//Add the y axis
var yaxis = svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y)
.ticks(5)
.tickSizeInner(0)
.tickPadding(6)
.tickSize(0, 0));
//Add y axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - 60)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Price")
.attr("class", "y axis label");
}
// Create initial graph
initialGraph("1301.T")
// Update the data
var updateGraph = function(stock){
// Filter the data to include only stock of interest
var selectStock = nest.filter(function(d){
return d.key == stock;
})
console.log(selectStock);
//Unnest selectStock for y axis
var unnested = function(data, children){
var out = [];
data.forEach(function(d, i){
console.log(i, d);
d_keys = Object.keys(d);
console.log(i, d_keys)
values = d[children];
values.forEach(function(v){
d_keys.forEach(function(k){
if (k != children) { v[k] = d[k]}
})
out.push(v);
})
})
return out;
}
var selectStockUnnested = unnested(selectStock, "values");
// Select all of the grouped elements and update the data
var selectStockGroups = svg.selectAll(".stockGroups")
.data(selectStock)
.each(function(d){
y.domain([0, d3.max(selectStockUnnested, function(d) { return d.value; })])
});
// Select all the lines and transition to new positions
selectStockGroups.selectAll("path.line")
.data(selectStock)
.transition()
.duration(1000)
.attr("d", function(d){
return valueLine(d.values)
})
// Update the Y-axis
d3.select(".y")
.transition()
.duration(1500)
.call(d3.axisLeft(y)
.ticks(5)
.tickSizeInner(0)
.tickPadding(6)
.tickSize(0, 0));
}
// Run update function when dropdown selection changes
dropDown.on('change', function(){
// Find which stock was selected from the dropdown
var selectedStock = d3.select(this)
.select("select")
.property("value")
console.log(selectedStock);
// Run update function with the selected stock
updateGraph(selectedStock)
});
});
</script>
</body>
I have the following d3 code:
var json = [
{
date: "05/17",
numTags: 23
}
];
d3.select('summary-graph').selectAll('*').remove();
var svg = d3.select("summary-graph"),
margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom;
var parseTime = d3.timeParse("%m/%y");
var svg = d3.select("summary-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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function (d) {
console.log(d);
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function (d) {
return d.date;
}))
.nice();
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function (d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function (d) {
return xScale(d.date);
})
.y(function (d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) { return "blue" })
.attr("cx", function(d, i) { return xScale(d.date) })
.attr("cy", function(d, i) { return yScale(d.numTags) })
.attr("r", function(d, i) { return 10 });
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size","14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
Which produces the following visualization:
I'm trying to figure out how to center the tick mark and show the x-axis label when there is only 1 data point like this. At 2 data points, I don't love how it still sets the tick marks at the extreme ends of the x-axis. For 3 data points and above, it looks nice (thanks to .nice() it seems).
Any help?
Based on Gerado's response, I was able to get close. The last sticking point is that the left side of the X-axis now has the month (March) despite no data for that day.
Fixed:
changed .ticks(json.length) to .ticks(d3.timeMonth.every(1))
Since you are using only a single data point, your date scale has a domain in which the lower and upper values are the same:
[
Mon May 01 2017 00: 00: 00 GMT + 1000,
Mon May 01 2017 00: 00: 00 GMT + 1000
]
For putting that circle in the middle of the x axis you have to set different values for the scale's domain.
There are several ways for doing that. My proposed solution here involves verifying if the domain's values are the same...
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
... and, if they are, changing them. In this case, I'm subtracting one day from the lower limit and adding one day to the upper limit:
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
Check the result:
var json = [{
date: "05/17",
numTags: 23
}];
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 400,
height = 200;
var parseTime = d3.timeParse("%m/%y");
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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.date;
}))
.nice();
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) {
return "blue"
})
.attr("cx", function(d, i) {
return xScale(d.date)
})
.attr("cy", function(d, i) {
return yScale(d.numTags)
})
.attr("r", function(d, i) {
return 10
});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size", "14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
<script src="https://d3js.org/d3.v4.js"></script>
EDIT: As you asked in your edit, when you have two data values my solution will create additional ticks on the limits, which is the expected behaviour:
var json = [{
date: "05/17",
numTags: 23
}, {
date: "05/17",
numTags: 17
}];
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 400,
height = 200;
var parseTime = d3.timeParse("%m/%y");
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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.date;
}))
.nice();
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) {
return "blue"
})
.attr("cx", function(d, i) {
return xScale(d.date)
})
.attr("cy", function(d, i) {
return yScale(d.numTags)
})
.attr("r", function(d, i) {
return 10
});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size", "14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
<script src="https://d3js.org/d3.v4.js"></script>
There are several ways for removing those ticks. One of them is using tickValues:
.tickValues(data.map(function(d){ return d.date}))
Here is the demo:
var json = [{
date: "05/17",
numTags: 23
}, {
date: "05/17",
numTags: 17
}];
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 400,
height = 200;
var parseTime = d3.timeParse("%m/%y");
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 + ")");
// Get the data
var data = json;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.numTags = +d.numTags;
});
// set the ranges
var xScale = d3.scaleTime()
.range([0, width])
.domain(d3.extent(data, function(d) {
return d.date;
}))
.nice();
if (xScale.domain()[0].getTime() == xScale.domain()[1].getTime()) {
var dateLess = d3.timeDay.offset(xScale.domain()[0], -1);
var dateMore = d3.timeDay.offset(xScale.domain()[0], 1);
xScale.domain([dateLess, dateMore])
}
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, d3.max(data, function(d) {
return Math.max(d.numTags);
})])
.nice();
// define the 1st line
var tagLine = d3.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.numTags);
});
// Axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(json.length)
.tickSizeOuter(0)
.tickValues(data.map(function(d){ return d.date}))
.tickFormat(d3.timeFormat('%B %Y'));
var yAxis = d3.axisLeft().scale(yScale);
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "blue")
.attr("d", tagLine);
var points = svg.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("stroke", "green")
.attr("fill", function(d, i) {
return "blue"
})
.attr("cx", function(d, i) {
return xScale(d.date)
})
.attr("cy", function(d, i) {
return yScale(d.numTags)
})
.attr("r", function(d, i) {
return 10
});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.style("font-size", "14px");;
// Add the Y Axis
svg.append("g")
.call(yAxis);
<script src="https://d3js.org/d3.v4.js"></script>
I have a line graph which looks fine. The data set is mostly random data with one big spike. The second dataset is just the log2 of each value. I can transition between the two and it looks great. The y axis is transitioning too. But the scale of the line is not transitioning. Not sure how to get the line to update with the right scale.
$(function() {
var margin = { top: 300, right: 100, bottom: 100, left: 100 },
width = 1400 - margin.right - margin.left,
height = 1080 - margin.top - margin.bottom;
var parseTime = d3.timeParse("%H:%M:%S");
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var log2y = d3.scaleLinear().range([height, 0]);
var valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.value); });
var log2valueline = d3.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(Math.log2(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.select("body").attr("align", "center");
d3.csv("data.csv", function (error, data) {
if (error) {
throw error;
}
var log2data = [];
var log10data = [];
data.forEach(function (d, index) {
d.date = parseTime(d.date);
d.value = +d.value;
log2data[index] = { "date": d.date, "value": Math.log2(+d.value) };
log10data[index] = { "date": d.date, "value": Math.log10(+d.value) };
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.value; })]);
log2y.domain([0, d3.max(log2data, function (d) { return d.value; }) ]);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
svg.append("g")
.attr("class", "xaxis")
.attr("class", "chart")
.attr("transform", "translate(0, " + height + ")")
.call(d3.axisBottom(x).ticks(d3.timeMinute.every(10)));
var yaxis = svg.append("g")
.attr("class", "yaxis")
.attr("class", "chart")
.call(d3.axisLeft(y).ticks(5));
yaxis
.transition().duration(500).delay(2000)
.call(d3.axisLeft(log2y).ticks(4));
svg.selectAll("path")
.data([log2data])
.transition().duration(500).delay(2000)
.attr("d", valueline);
});
});
So here is the chart before the transition:
And here is the chart after the transition:
Adding the update from Harpal, I see this:
It's because the new line is still using the old scale (valueline)
Change this:
svg.selectAll("path")
.data([log2data])
.transition().duration(500).delay(2000)
.attr("d", valueline);
to this:
svg.selectAll("path")
.data([log2data])
.transition().duration(500).delay(2000)
.attr("d", log2valueline);
So the solution, as with so many things, is to go for a walk, get something to eat, and return with a fresh look. I rewrote it, based on what I learned thus far and have a working solution:
$(function () {
var margin = { top: 300, right: 100, bottom: 100, left: 100 },
winwidth = $(window).width(),
winheight = $(window).height(),
width = winwidth - margin.right - margin.left,
height = winheight - margin.top - margin.bottom,
x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]);
var parseTime = d3.timeParse("%H:%M:%S");
var valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.value); });
var svg = d3.select('body').append('svg')
.attr("width", winwidth)
.attr("height", winheight)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var path = svg.append("path");
var xaxis = svg.append("g")
.attr("class", "xaxis")
.attr("class", "chart")
.attr("transform", "translate(0, " + height + ")");
var yaxis = svg.append("g")
.attr("class", "yaxis")
.attr("class", "chart");
function step1(sourcedata) {
var data = sourcedata.data;
var y = d3.scaleLinear().range([height, 0]);
y.domain([0, d3.max(data, function (d) { return Math.log2(d.value); })]);
valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function (d) { return x(d.date); })
.y(function (d) { return y(Math.log2(d.value)); });
path
.data([data])
.transition()
.attr("class", "line")
.attr("d", valueline);
yaxis
.transition()
.call(d3.axisLeft(y).ticks(4));
}
d3.select("body").attr("align", "center");
d3.csv("data.csv", function (error, data) {
if (error) {
throw error;
}
data.forEach(function (d) {
d.date = parseTime(d.date);
d.value = +d.value;
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.value; })]);
path
.data([data])
.attr("class", "line")
.attr("d", valueline);
xaxis
.attr("transform", "translate(0, " + height + ")")
.call(d3.axisBottom(x).ticks(d3.timeMinute.every(10)));
yaxis.call(d3.axisLeft(y).ticks(5));
$("#rescale").click(data, step1);
});
});
And the graph works as follows:
I have some data ranging from 2003 - 2007 plotted as a line graph using the following code:
var x = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]);
var area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.price); });
and later when I've pulled my data in:
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
context.append("path")
.datum(data)
.attr("d", area);
I've implemented a brush to filter the data. It works like this one:
http://bl.ocks.org/mbostock/4349545
But instead of a scatterplot, I have a line graph. I want to select arbitrary portions of the graph area and recolour it based on the brush selection. Just like the second graph on this yahoo chart:
http://uk.finance.yahoo.com/echarts?s=SAN.MC#symbol=san.mc;range=20040915,20131007;compare=;indicator=volume;charttype=area;crosshair=on;ohlcvalues=0;logscale=off;source=undefined;
I have a brush function that's fired on brushmove:
function brushmove() {
var s = brush.extent();
console.log(s);
}
which logs out the selected range correctly. I'm just not sure how to select a portion of my graph area based on the range coming back from the brushmove function.
Full code is here:
//specify some margins
var margin = {top: 500, right: 10, bottom: 20, left: 40},
width = 960,
height = 100;
var bigMargin = {top: 20, right: 10, bottom: 20, left: 40},
bigWidth = 960,
bigHeight = 400;
//parse the date
var parseDate = d3.time.format("%b %Y").parse;
//create scales
var x = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]);
//create scales
var bigX = d3.time.scale().range([0, bigWidth]),
bigY = d3.scale.linear().range([bigHeight, 0]);
//create axis
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var bigXAxis = d3.svg.axis().scale(bigX).orient("bottom"),
bigYAxis = d3.svg.axis().scale(bigY).orient("left");
//create a brush area accessor function
var brush = d3.svg.brush()
.x(x)
.on("brush", brushmove);
//create an area accessor function
var area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.price); });
var bigArea = d3.svg.area()
.x(function(d) { return bigX(d.date); })
.y0(bigHeight)
.y1(function(d) { return bigY(d.price); });
//append the svg container
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", bigWidth)
.attr("height", bigHeight);
//append a container
var context = svg.append("g")
.attr('class','container')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bigContext = svg.append("g")
.attr('class','bigContainer')
.attr("transform", "translate(" + bigMargin.left + "," + bigMargin.top + ")");
//loop over the data
d3.csv("data.csv", function(error, data) {
//coerce the data a little because values from .CSv are always strings
data.forEach(function(d) {
d.date = parseDate(d.date);
d.price = +d.price;
});
//set the scale domains based on the data
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
bigContext.append("path")
.datum(data)
.attr("clip-path", "url(#clip)")
.attr("fill","red")
.attr("d", bigArea);
//set the scale domains based on the data
bigX.domain(d3.extent(data.map(function(d) { return d.date; })));
bigY.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
var t = new Date(2003, 0, 1);
//append the graph as an area
context.append("path")
.datum(data)
.attr("d", area).
attr("fill",function(d) {
//if the data is at a certain range then give it a particular fill
return (new Date(d.date) > t ? "orange" : "yellow"); });
//append the x axis
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
//append the x axis
context.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis);
//append the x axis
bigContext.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + bigHeight + ")")
.call(bigXAxis);
//append the x axis
bigContext.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(bigYAxis);
//append the brush
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height + 7)
});
function brushstart() {
svg.classed("selecting", true);
}
function brushmove() {
bigX.domain(brush.extent());
bigContext.select("path").attr("d", bigArea);
bigContext.select(".x.axis").call(bigXAxis);
}
I am a newbie with D3 library and I am stuck with zooming on a graph.
I display correctly my data over several graphs. But when I zoom, everything goes wrong. I don't know if I miss something with Domains or Ranges or anything... so I ask.
You can find a demo of my code here: http://pastehtml.com/view/cos13vodt.html
And here is the jsFiddle example: http://jsfiddle.net/84mSQ/
And my JS code is there:
var margin = {top: 30, right: 150, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//To parse dates as they are into the CSV
var parseDate = d3.time.format("%Y/%m/%d-%H:%M").parse;
var format = d3.time.format("%d/%m/%y-%H:%M");
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")/*.ticks(30)*/;
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 2])
.on("zoom", function(scale, translate){
console.log("fonction zoom");
console.log(scale); console.log(translate);
zoomed(scale, translate);
});
// A line generator.
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("svg:g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
svg.append("rect")
.attr("class", "pane")
.attr("width", width)
.attr("height", height);
// Get the data
d3.csv("./enregistrement-subset2.csv", function(data) {
color.domain(d3.keys(data[0])
.filter(function(key) {
return key !== "date" && key !== "ECS - Button A" ;
}));
data.forEach(function(d) {
//Parse the date
d.date = parseDate(d.date);
});
var dataSet = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
//parses the number by using the '+' operator
if(name == "CO2 chambre"){
return { date: d.date, value: (+d[name])/10};
}
else{
return { date: d.date, value: +d[name]};
}
})
};
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(dataSet, function(c) { var mini = d3.min(c.values, function(v) { return v.value; }); return mini; }),
d3.max(dataSet, function(c) { var maxi = d3.max(c.values, function(v) { return v.value; }); return maxi; })
]);
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("Value of");
var valueSet = svg.selectAll(".valueSet")
.data(dataSet)
.enter().append("g")
.attr("class", "valueSet");
valueSet.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); })
.call(line);
valueSet.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.value) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
//zoomed();
});
function zoomed() {
console.log("here", d3.event);
svg.select("g.x.axis").call(xAxis);
svg.select("g.y.axis").call(yAxis);
//svg.selectAll("path.line").call(line);
svg.selectAll("path.line").attr("d", line);
//d3.select("#footer span").text("PĂ©riode de temps: " + x.domain().map(format).join("-"));
}
Can anybody tell me what I did wrong with this code ?
Should I re-design it ?
Are there performances issue to preview if I use a huge amount of data and what should I do then ?
var zoom = d3.behavior.zoom()
.x(x)
**.scaleExtent([1, 2])** <---
.on("zoom", function(scale, translate){
console.log("fonction zoom");
console.log(scale); console.log(translate);
zoomed(scale, translate);
});
take out the .scaleExtent([1, 2]) and check if it is working
should be
var zoom = d3.behavior.zoom()
.x(x)
.on("zoom", function(scale, translate){
console.log("fonction zoom");
console.log(scale); console.log(translate);
zoomed(scale, translate);
});
it is because your x axis is time.