Hello I try to have a similar chart like this : http://jsfiddle.net/chrisJamesC/tNdJj/4/
I want to draw a linechart in a svg with possible negative values and I want the same axis of the jsfiddle.
I put my code here :
var lineData = [10,2,3,4,-9,12,1000,-1000,12,22,15];
var svg = d3.select("#chart_diff");
var margin = {top: 30, right: 10, bottom: 10, left: 30},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
xRange = d3.scale.linear().range([0,width]).domain([0,lineData.length]);
var yMax = d3.max(lineData, function (d) { return d;});
yRange = d3.scale.linear()
.range([height, 0])
.domain([-yMax, yMax]);
xAxis = d3.svg.axis()
.scale(xRange)
.tickSize(5)
.tickSubdivide(true),
yAxis = d3.svg.axis()
.scale(yRange)
.tickSize(5)
.orient("left")
.tickSubdivide(true);
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - margin.bottom) + ")")
.call(xAxis);
svg.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis);
svg.selectAll("g")
.style("stroke-width","3px");
var lineFunc = d3.svg.line()
.x(function(d,i) {
return xRange(i);
})
.y(function(d) {
return yRange(d);
})
.interpolate('linear');
svg.append('svg:path')
.attr('d', lineFunc(lineData))
.attr('stroke', 'blue')
.attr('stroke-width', 2)
.attr('fill', 'none');
With the jsfiddle : http://jsfiddle.net/0zj76je5/
I hope anyone can help me !
Related
I'm creating a bar chart in d3 (using r2d3 in R) that shows data per month. I'm wondering how I can format the x-axis so that while there is a bar for each month, the labels only show the year markers (i.e. under each january bar.)
Data look like this:
letter,frequency
1/1/2017,144
2/1/2017,85
3/1/2017,59
4/1/2017,73
5/1/2017,68
6/1/2017,91
7/1/2017,107
8/1/2017,94
9/1/2017,79
10/1/2017,84
11/1/2017,70
12/1/2017,86
1/1/2018,72
2/1/2018,71
3/1/2018,82
4/1/2018,50
5/1/2018,86
6/1/2018,75
7/1/2018,62
8/1/2018,72
9/1/2018,65
10/1/2018,75
11/1/2018,63
12/1/2018,87
1/1/2019,59
2/1/2019,60
3/1/2019,99
4/1/2019,81
Data in console:
D3 Code:
var parseDate = d3.time.format("%Y-%m-%d").parse;
//coerce data to dates
data.forEach(function (d) {
d.letter = parseDate(d.letter);
console.log(d.letter)
});
//set margins
var margin = {top: 40, right: 20, bottom: 60, left: 40},
width = 1000 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
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")
.tickFormat(d3.time.format("%Y-%m"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
var svg = 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 + ")");
r2d3.onRender(function(data, s, w, h, options) {
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)
.selectAll("text")
.attr("transform", "rotate(270)")
.attr('dx', '-1.9em');
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); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
});
function type(d) {
d.frequency = +d.frequency;
console.log(d);
return d;
}
Current Chart:
What I want:
Here's how D3 suggests filtering ordinal scales. This should only show your January ticks
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickValues(x.domain().filter(function(d, i) { return !(i % 12); }))
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.
This is my very first to d3js.I have use this d3js Line Chart Sample.But after feeding the data it doesn't draw the chart but i can see the data has been loaded by using the firebug.But the data doesn't print in the graph at all. Could n't figure out the problem.Any help will be really appreciated.
This is My code,
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("%d-%b").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.timeStamp);
})
.y(function(d) {return y(d.memberAverageLoadAverage); });
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 json1=[
{
"clusterId": "",
"timeStamp": 1437063744524,
"memberAverageLoadAverage": 20,
"memberId": ""
},
{
"clusterId": "",
"timeStamp": 1437069850060,
"memberAverageLoadAverage": 20,
"memberId": ""
},
{
"clusterId": "",
"timeStamp": 1437069910059,
"memberAverageLoadAverage": 20,
"memberId": ""
},
{
"clusterId": "",
"timeStamp": 1437069970060,
"memberAverageLoadAverage": 20,
"memberId": ""
},
{
"clusterId": "",
"timeStamp": 1437070030056,
"memberAverageLoadAverage": 20,
"memberId": ""
}
];
root = json1;
x.domain(d3.extent(root, function(d) { return d.timeStamp; }));
y.domain(d3.extent(root, function(d) { return d.memberAverageLoadAverage; }));
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("memberAverageLoadAverage");
svg.append("path")
.datum(root)
.attr("class", "line")
.attr("d", line);
After removing the JSON.parse for the JS object and changing the memberAverageLoadAverage to not all be the same value, I was able to display a graph. The reason I needed to change the memberAverageLoadAverage values is that the extent call was making the y axis go from 20 to 20 so the line was at the bottom of the screen and collided with the x axis line. I would suggest setting the y.domain to [0, d3.max(...)] instead of using d3.extent.
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("%d-%b").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.timeStamp);
})
.y(function(d) {return y(d.memberAverageLoadAverage); });
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 json1=[{"clusterId":"","timeStamp":1437063744524,"memberAverageLoadAverage":11,"memberId":""},{"clusterId":"","timeStamp":1437069850060,"memberAverageLoadAverage":5,"memberId":""},{"clusterId":"","timeStamp":1437069910059,"memberAverageLoadAverage":6,"memberId":""},{"clusterId":"","timeStamp":1437069970060,"memberAverageLoadAverage":15,"memberId":""},{"clusterId":"","timeStamp":1437070030056,"memberAverageLoadAverage":20,"memberId":""}];
var data = json1;
x.domain(d3.extent(data, function(d) { return d.timeStamp; }));
y.domain([0, d3.max(data, function(d) { return d.memberAverageLoadAverage; })]);
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);
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Should be a comment, but I suggest a couple of things. 1. I suggest using externally loaded JSON data if you have a server you can use to get the http request. If not, then you can use local data, but either way you have to have a data.foreach function in which you create all of your values, similar to the oneseen below.
data.foreach(function(d) {
d.timeStamp = d.timeStamp;
d.memberAverageLoadAverage = +d.memberAverageLoadAverage;
});
I also strongly suggest you reformat your JSON because your keys are not done phenomenally well, so I suggest you take a look here and reformat the data. Let me know how you do and what else I can help you with.
I am not sure if you can parse this. It is an array. Anyways, even if you can, I don't think there is a need to parse it.
`var json1=`[{"clusterId":"","timeStamp":1437063744524,"memberAverageLoadAverage":20,"memberId":""},
I am working on a multi-series line chart using d3.js and I am attempting to implement the focus and context zoom as seen in this example. I have converted the area chart in the example to a line chart with a single series but I cannot figure out how to extend it to allow multiple series.
Here is the code I am using:
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%b %Y").parse;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var line = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.price); }); // single series?
var line2 = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x2(d.date); })
.y(function(d) { return y2(d.price); });
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", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.csv("trends.csv", type, function(error, data) {
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.price; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line2);
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
});
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.select(".line").attr("d", line);
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.date = parseDate(d.date);
d.price = +d.price;
return d;
}
I was successful in building a multi-line chart without the focus+context zoom feature, but I would really like to get the zoom piece working if possible.
Any help is greatly appreciated!
Assuming from your data that you have multiple series for price, you would have to change d.price to d[price]. Did you check this stack overflow question? I think it's what you're looking for.
I have implemented the Drawing Focus + Context via Brushing diagram to display the chi-square results, but the drawing of the results in d3 are strange (check image below at 7 AM).
Also below the image I have included the code which draws the diagram.
ChiSquare Image
var chiSquare = function(config,data,d3){
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 1127 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%b %Y").parse;
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var area = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x(d.timestamp_unix); })
.y0(height)
.y1(function(d) { return y(d.chiSquare); });
var area2 = d3.svg.area()
.interpolate("monotone")
.x(function(d) { return x2(d.timestamp_unix); })
.y0(height2)
.y1(function(d) {return y2(d.chiSquare); });
var svg = d3.select(config.selector).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", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
x.domain(d3.extent(data,(function(d) { return d.timestamp_unix; })));
y.domain([0, d3.max(data, (function(d) { return d.chiSquare; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
context.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area2);
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.select(".area").attr("d", area);
focus.select(".x.axis").call(xAxis);
}
function type(d) {
d.timestamp_unix = parseDate(d.timestamp_unix);
d.chiSquare = +d.chiSquare;
return d;
}
//end class
}
I can't see anything wrong in the code, so I assume that the problem is from D3js but I don't know where to look, any ideas?