I'm creating a time-based line chart and everything looks fine but I'm having difficulty with the x-axis tickmarks. As you can see http://jsfiddle.net/shubo/Yvupw/ the dates start at 2013-01-30 and ends at 2013-04-17 but the chart tick mark starts at 2013-2-3 and ends at 2013-4-14. What do I need to do so the first tickmark would show 2013-01-03 and the last one would show 2013-4-17?
var json = {
"data": [
{
"date": "2013-04-17",
"metric": 437792798
},
{
"date": "2013-04-10",
"metric": 437792998
},
{
"date": "2013-04-03",
"metric": 434633203
},
{
"date": "2013-03-27",
"metric": 431786310
},
{
"date": "2013-03-20",
"metric": 429614980
},
{
"date": "2013-03-13",
"metric": 427709519
},
{
"date": "2013-03-06",
"metric": 425894908
},
{
"date": "2013-02-27",
"metric": 423657524
},
{
"date": "2013-02-20",
"metric": 420392146
},
{
"date": "2013-02-13",
"metric": 417215035
},
{
"date": "2013-02-06",
"metric": 412433066
},
{
"date": "2013-01-30",
"metric": 408952856
}
]
};
var margin = {top: 20, right: 50, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 110 - 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 line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.metric); });
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 = json.data;
data.forEach(function(d) {
d.date = parseDate(d.date);
});
data.sort(function(a, b) {
return a.date - b.date;
});
x.domain([data[0].date, data[data.length - 1].date]);
y.domain(d3.extent(data, function(d) { return d.metric; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
var format = d3.format(',');
Try using axis.tickValues([values]) to control exactly which tick marks appear or use one of the one of the other tick settings.
Related
I have a stackbiltz here - https://stackblitz.com/edit/d3-start-above-zero-gtudz8?embed=1&file=index.js&hideNavigation=1
I'm getting an error in the console.
Error: <path> attribute d: Expected number
The console log in the code show the correct data but I dont think its getting passed to the line function.
Couple obvious issues:
Your X values are strings but you are using a scaleLinear. That's for numbers; try scaleBand.
With that fixed, you aren't setting your x domain properly.
Here's those two fixes:
var data = [
{
"date": "Jan",
"value": 1507
},
{
"date": "Feb",
"value": 1600
},
{
"date": "Mar",
"value": 1281
},
{
"date": "Apr",
"value": 1898
},
{
"date": "May",
"value": 1749
},
{
"date": "June",
"value": 1270
},
{
"date": "July",
"value": 1712
},
{
"date": "Aug",
"value": 1270
},
{
"date": "Sept",
"value": 1257
},
{
"date": "Oct",
"value": 1257
},
{
"date": "Nov",
"value": 1257
},
{
"date": "Dec",
"value": 1257
}
];
///////////////////////////// Create SVG
var w = 400;
var h = 250;
var margin = {
top: 20,
bottom: 20,
left: 40,
right: 20
}
var width = w - margin.left - margin.right
var height = h - margin.top - margin.bottom
var svg = d3.select("body").append("svg")
.attr("id", "svg")
.attr("width", w)
.attr("height", h)
var chart = svg.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
///////////////////////////// Create Scale
var x = d3.scaleBand()
.range([0, width])
var y = d3.scaleLinear()
.rangeRound([height, 0])
///////////////////////////// Create Line
var line = d3.line()
.x(function(d) {
console.log(d.date)
console.log(x.domain())
return x(d.date)
})
.y(function(d) {
console.log(d.value)
console.log(y.domain())
return y(d.value)
})
x.domain(data.map(function(d) {
return d.date
}));
y.domain([0, d3.max(data, function(d) {
return d.value
})]);
chart.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
///////////////////////////// Create Axis
var xAxis = chart.append('g')
.classed('x-axis', true)
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
var yAxis = chart.append('g')
.classed('y-axis', true)
.call(d3.axisLeft(y))
<script src="https://d3js.org/d3.v4.min.js"></script>
I am having a hard time in finding the points in between the given X scale domain.
For eg. I have a bar chart having its X axis values as
[Bob,Robin,Anne,Mark,Joe,Eve,Karen,Kirsty,Chris,Lisa,Tom,Stacy,Charles,Mary]
The above values are used to plot the x-axis.I have used scaleBand method since the values are discrete.
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
x.domain(data.map(function(d) { return d.salesperson; }));
My Aim is to find points in between x scale from the given points,i.e
Is there any way that we can locate a point between Bob,Robinor Robin,Anne, So that it would be possible for me to plot a point provided a Y axis value is given.
I have added a plunker which illustrates bar chart having the mentioned x-axis.I need to plot a point on the chart having X value point between Bob,Robin
and Y value as 30(any value on Y axis)
Provided that Robin is the discrete value just after Bob, you can use a combination of bandwidth() and step():
x("Bob") + x.bandwidth() + (x.step()-x.bandwidth()) / 2
Ugly, but it works. Talking about ugliness, if you want an uglier math, you can also use a combination of step() and paddingInner():
x("Bob") + x.step() * (1 - x.paddingInner() / 2))
Here is a demo, I put the point at y(10) so you can better see that it will be right between the "Bob" and "Robin" bars:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
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 = [
{
"salesperson": "Bob",
"sales": 33
},
{
"salesperson": "Robin",
"sales": 12
},
{
"salesperson": "Anne",
"sales": 41
},
{
"salesperson": "Mark",
"sales": 16
},
{
"salesperson": "Joe",
"sales": 59
},
{
"salesperson": "Eve",
"sales": 38
},
{
"salesperson": "Karen",
"sales": 21
},
{
"salesperson": "Kirsty",
"sales": 25
},
{
"salesperson": "Chris",
"sales": 30
},
{
"salesperson": "Lisa",
"sales": 47
},
{
"salesperson": "Tom",
"sales": 5
},
{
"salesperson": "Stacy",
"sales": 20
},
{
"salesperson": "Charles",
"sales": 13
},
{
"salesperson": "Mary",
"sales": 29
}
];
// format the data
data.forEach(function(d) {
d.sales = +d.sales;
});
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.salesperson; }));
y.domain([0, d3.max(data, function(d) { return d.sales; })]);
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.salesperson); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.sales); })
.attr("height", function(d) { return height - y(d.sales); });
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
var pointBetween = svg.append("circle")
.attr("r", 2)
.attr("cy", y(10))
.attr("cx", x("Bob") + x.bandwidth() + (x.step()-x.bandwidth())/2)
.bar { fill: steelblue; }
<script src="//d3js.org/d3.v4.min.js"></script>
I am learning d3 and trying to implement a specific functionality.
Data is not getting plotted properly. I am not able to find where it is going wrong.
Link to jsfiddle https://jsfiddle.net/4nc1nc95/1/
$(function() {
var width = 355;
var height = 142;
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 70
};
var scope = {
graphData: {
"data": [{
"YEAR": "FY16Q1",
"SAVINGS": null,
"SPEND": null
}, {
"YEAR": "FY16Q2",
"SAVINGS": null,
"SPEND": null
}, {
"YEAR": "FY16Q3",
"SAVINGS": null,
"SPEND": null
}, {
"YEAR": "FY16Q4",
"SAVINGS": "0.023961",
"SPEND": "7419879.04"
}, {
"YEAR": "FY17Q1",
"SAVINGS": "0.00618",
"SPEND": "34923499.71732"
}]
}
};
var x_domain = d3.extent(scope.graphData.data, function(d) {
return d.YEAR;
});
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .0)
.domain(scope.graphData.data.map(function(d) {
return d.YEAR;
}));
var y = d3.scale.linear()
.domain([0, 0.03])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5, "%");
var area = d3.svg.area()
.x(function(d) {
return x(d.YEAR);
})
.y0(height)
.y1(function(d) {
return y(d.SAVINGS);
});
var sampleSVG = d3.select('#area')
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + 62)
.append("g")
.attr("transform", "translate(" + margin.left + "," + (margin.top + 24) + ")");
sampleSVG.append("path")
.datum(scope.graphData.data)
.attr("class", "area")
.attr("d", area);
sampleSVG.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
sampleSVG.append("g")
.attr("class", "y axis")
.call(yAxis);
if (scope.mode == "restore") {
sampleSVG.append("g")
.append("text")
.attr("y", "-8%")
.attr("x", "-2%")
.attr("class", "heading tableHeader")
.text("SAVING");
}
})
Instead of:
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .0)
Use:
var x = d3.scale.ordinal()
.rangePoints([0, width], .0)
Here is your fiddle: https://jsfiddle.net/gerardofurtado/b1mzyxLq/
I am new in D3 js.most of the examples in gallery load data from TSV files.
but I wanted to render line graph using json in mvc3 razor. below is code where i hard code my data.please let me know how to retrive dynamic data using json.
var data = [
{ "date": 0, "close": 0.3372 },
{ "date": 1, "close": 1.7 },
{ "date": 2, "close": 1.8 },
{ "date": 3, "close": 2.014 },
{ "date": 4, "close": 10.995 },
{ "date": 5, "close": 16.227 },
{ "date": 6, "close": 16.643 },
{ "date": 7, "close": 20.644 },
{ "date": 8, "close": 22.478 }
];
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
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").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 + ")");
console.log("here");
data.forEach(function (d) {
d.date = parseInt(d.date);
d.close = +d.close;
});
console.log("here2");
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 + ")")
.text("Request")
.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("Probability");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
I´m working on an stacked area chart which shows energy use for the years 2011 and 2040.
Later I will make the chart interactive. The chart works, I used this example: http://bl.ocks.org/mbostock/3020685
But the Ticks are wrong. They show .020 .030 .040 instead of 2020 2030 and 2040.
I can´t find out where is the mistake. i tried some things but nothing worked.
Thank you for helping.
Here is my code:
function szenario () {
var data = [{"key":"Strom (fossil)", "value": 30, "date": 2011},
{"key":"Strom aus Biomasse", "value": 3, "date": 2011},
{"key":"Strom aus Windkraft", "value": 5, "date": 2011},
{"key":"Strom aus Photovoltaik", "value": 2, "date": 2011},
{"key":"Strom aus Wasserkraft", "value": 4, "date": 2011},
{"key":"Wärme aus Biomasse", "value": 2, "date": 2011},
{"key":"Wärme aus Solarenergie", "value": 1, "date": 2011},
{"key":"Wärme (fossil)", "value": 40, "date": 2011},
{"key":"Bio-Treibstoffe", "value": 5, "date": 2011},
{"key":"Treibstoffe-fossil", "value": 45, "date": 2011},
{"key":"Strom (fossil)", "value": 0, "date": 2040},
{"key":"Strom aus Biomasse", "value": 20, "date": 2040},
{"key":"Strom aus Windkraft", "value": 30, "date": 2040},
{"key":"Strom aus Photovoltaik", "value": 15, "date": 2040},
{"key":"Strom aus Wasserkraft", "value": 5, "date": 2040},
{"key":"Wärme aus Biomasse", "value": 10, "date": 2040},
{"key":"Wärme aus Solarenergie", "value": 8, "date": 2040},
{"key":"Wärme (fossil)", "value": 0, "date": 2040},
{"key":"Bio-Treibstoffe", "value": 20, "date": 2040},
{"key":"Treibstoffe-fossil", "value": 0, "date": 2040}
];
//var formatX = d3.time.format("%Y");
var formatY = d3.format("");
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 520 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var z = d3.scale.category20c();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(3)
;
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatY);
var stack = d3.layout.stack()
.offset("zero")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.value; });
var nest = d3.nest()
.key(function(d) { return d.key; });
var area = d3.svg.area()
.interpolate("cardinal")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
var svg = d3.select("#Szenarioanzeige")
.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.map(data, function(data) {
data.forEach(function(d) {
d.date = formatX(d.date);
d.value = +d.value;
});
});*/
var layers = stack(nest.entries(data));
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return z(i); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
};
Uncomment the line
var formatX = d3.time.format("%Y");
And you can then parse the date like
> formatX.parse("2008")
Tue Jan 01 2008 00:00:00 GMT+0000 (GMT Standard Time)
You have to parse your years into Dates. To do that, run
d3.time.format("%Y").parse("" + d.date)
in every place where you currently have d.date.
The easiest way to parse every date is to have a parser function and loop it over the data.
var parseDate = d3.time.format("%Y).parse;
data.forEach(function(d) {
d.date = parseDate( d.date );
});