I've built a page with five d3.js charts. Four of them use standard linear axes (linear data over time), and the fifth chart uses log/log axes. So far, I can get either the linear charts to render correctly, or the log/log chart to render correctly, but not both.
Question -- how can I define log/log axes for the final chart while keeping the first four linear charts working correctly?
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// Overall chart settings
var divwidth = $("#collapseOne").width();
var divheight = $(window).height();
var margin = {top: 25, right: 60, bottom: 35, left: 80},
width = divwidth - margin.left - margin.right,
height = 0.65*divheight - margin.top - margin.bottom;
var formatDate = d3.time.format("%Y-%m-%d");
var x = d3.time.scale()
.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");
</script>
Then I define the first chart:
<script>
// Chart1 - NumofCompanies chart settings
var line1 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.cumcos); });
var chart1 = d3.select("#collapseOne")
.append("div")
.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("/monthly.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.cumcos; }));
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart1.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 companies");
chart1.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line1);
});
function type(d) {
d.date = formatDate.parse(d.date);
d.cumcos = +d.cumcos;
return d;
}
</script>
and then the log/log chart
<script>
// Chart5 - Log/log chart
var x = d3.scale.log()
.range([0, width]);
var y = d3.scale.log()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
.y(function(de) { return y(de.sumfunding); });
var chart5 = d3.select("#collapseFive").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("/loglog.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(de) { return de.ordernum; }));
y.domain(d3.extent(data, function(de) { return de.sumfunding; }));
chart5.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart5.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("Sum of Funding ($)");
chart5.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line5);
});
function type(de) {
de.ordernum = +de.ordernum;
de.sumfunding = +de.sumfunding;
return de;
}
</script>
edit - I've also tried renaming variables like this below and it still doesn't work; the axes are still linear. And when I try changing things like x.domain to xlog.domain and .x(function(de to .xlog(function(de then nothing on the chart renders at all.
<script>
// Chart5 - Log/log chart
var xlog = d3.scale.log()
.range([0, width]);
var ylog = d3.scale.log()
.range([height, 0]);
var xAxislog = d3.svg.axis()
.scale(xlog)
.orient("bottom");
var yAxislog = d3.svg.axis()
.scale(ylog)
.orient("left");
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
.y(function(de) { return y(de.sumfunding); });
var chart5 = d3.select("#collapseFive").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("/loglog.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(de) { return de.ordernum; }));
y.domain(d3.extent(data, function(de) { return de.sumfunding; }));
chart5.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxislog);
chart5.append("g")
.attr("class", "y axis")
.call(yAxislog)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Sum of Funding ($)");
chart5.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line5);
});
function type(de) {
de.ordernum = +de.ordernum;
de.sumfunding = +de.sumfunding;
return de;
}
</script>
You are mistaking the first x in the line generator with the variable x in your scale:
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
//^--this has to be "x" ^--this is the scale
So, it has to be:
var line5 = d3.svg.line()
.x(function(de) { return xLog(de.ordernum); })
.y(function(de) { return yLog(de.sumfunding); });
According to the API of version 3:
line.x - get or set the x-coordinate accessor.
line.y - get or set the y-coordinate accessor.
As a good practice, avoid variable names like x. Instead, use names like xScale, xAxis, xPosition etc.
Related
I´m using d3.js library but i´m having so problems with linear graphics. It looks like on each page refresh, the graphics is replicated, and after some running time i got lots of duplicated graphics. I´m also using static data (i entered some random data for the graphic to work). Can someone help me? Why is this happening? Any solutions?
chart3-controller.js
module.exports = function Chart3Ctrl($scope) {
d3 = require('d3')
$scope.chart = function() {
var arrData = [
["2012-10-02", 200],
["2012-10-09", 300],
["2012-10-12", 150]
];
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
width = 960 - 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 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("#chart3").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 = arrData.map(function(d) {
return {
date: parseDate(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);
}
}
chart3.pug
.widget-container.fluid-height.stf-cpu(ng-controller='Chart3Ctrl')
.heading
i.fa
span(translate) Chart 3
.widget-content.padded
div#chart3(style='width:100%; height:100%;')
{{ chart() }}
Please any help welcome! :)
Thanx
Have you tried to remove the chart at the top of the function?
so;
d3.selectAll('#chart3 svg').remove();
this would remove any instance of the chart before re drawing, a better approach might be to wrap inside an if statement to check if the svg exists first and then drawing it if it doesn't, using jquery this could be ;
if(!$('#chart3 svg').length > 0) {
run code here...
}
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 am having a lot of trouble with D3 bar chart where the length of the data I get from search is variable. I used the D3 bar chart example and built this. However as data length goes up, the graph gets less and less legible. The problem seems to be the range gets confused when it is beyond 100 points or so.
Code is below:
function makegraph(data,ctype) {
//var data=xdata.splice(-900)
var gwidth=data.length*2;
if(gwidth < 800) gwidth=800;
//gwidth=800
var margin = {top: 9, right: 20, bottom: 30, left: 90},
width = gwidth - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
//x=d3.scale.linear().domain([0,1]).range(0,width)
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(function(d) { if (d % 10) return ""; else return d;})
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// .ticks(10, "%");
d3.select("svg").remove()
var svg = d3.select("#graphchart").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 line = d3.svg.line()
.x(function(d) { return x(d.rowid); })
.y(function(d) { return y(d[ctype]); });
x.domain(data.map(function(d) { return d.rowid; }));
var ymax=d3.max(data, function(d) { return d[ctype]; });
var ymin=d3.min(data, function(d) { return d[ctype]; });
y.domain([ymin, ymax]);
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", Math.log10(ymax))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Bytes");
// console.log(x.rangeBand())
var bar = svg.selectAll(".bar")
.data(data);
bar.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.rowid); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d[ctype]); })
.attr("height", function(d) { var xya= height - y(d[ctype]); /* console.log(xya+":"+d[ctype]); */ return height - y(d[ctype]); })
/* .attr("title", function(d) { return d['bytes']+" Bytes at "+d.stime; }) */
.on("mouseover", function(d) { $("#graphinfo").html(d3.format('0,000')(d['bytes'])+" Bytes at "+d.stime) })
.on("mouseout",function(d) { $("#graphinfo").html(' ')})
.on("click",function(d) { tablegraph(d)});
$('.bar').tooltip()
}
Do you have suggestions how I can make this graph so that the bar chart will grow horizontally for larger data sets? Thanks for any help and suggestions!
Vijay
I am trying to plot a bar chart by taking two columns from my database and converting it to JSON using json_encode in PHP (probc.php file)
I referred to this example here to construct a bar chart - BAR CHART EXAMPLE
After doing so, I changed the attributes letters and frequency to the column names I wanted to use (fphour for x-axis and fpprob for y-axis), and also changed d3.tsv to d3.json and included the relevant file (probc.php). But I do not get any results on running my file. Just a blank vanilla page. Please help!
Here's the code
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
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 xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "%");
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("probc.php", type, function(error, data) {
x.domain(data.map(function(d) { return d.fphour; }));
y.domain([0, d3.max(data, function(d) { return d.fpprob; })]);
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.fphour); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.fpprob); })
.attr("height", function(d) { return height - y(d.fpprob); });
});
function type(d) {
d.fpprob = +d.fpprob;
return d;
}
</script>
Type should not be here .
d3.json("probc.php", ***type***, function(error, data) {
Remove type
d3.json("probc.php", function(error, data) {
I currently have a web app which adds a new d3 chart to the page upon every click of submit (after defining parameters). What I'd like to do is add the newest chart above the others and push the older ones down to allow space. It tried changing the svg append to prepend but it's obviously not going to be that simple!
Here's my script;
function chartGenerator(file_location)
{
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
var x = d3.time.scale()
.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").prepend("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("{% static 'dgconnection/data.csv' %}", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
svg.prepend("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.predend("g")
.attr("class", "y axis")
.call(yAxis)
.prepend("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value");
svg.prepend("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
}
You can prepend elements to existing elements in D3, but the function isn't called prepend(), but insert() and takes two arguments. The first is, like for .append(), the name of the element to insert. The second is a selector. The new element will be inserted before the element that matches the selector.
In your case, the only code you need to change is this:
var svg = d3.select("body").insert("svg", "svg")