I have a line chart built in d3.js. I needed some help with some customisation. I am looking to split x-axis text labels in two lines.
I want the date in one line and the month in another.
The present chart has "14 Dec" in one line.
The present chart:
The x-axis labels are split into 2 lines here. Date and month in 2 different lines.
Expected x-axis:
Codepen link
var xScale = d3.time.scale().domain([data[0][xkeyVal], data[data.length - 1][xkeyVal]]).range([margin.left, width]);
var yScale = d3.scale.linear().domain([0, d3.max(data, function(d) {
return d[ykeyVal];
})]).range([height, margin.left]);
var xAxisGen = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(_config.keys.xAxis.ticks)
.tickFormat(d3.time.format("%d %b"))
.tickSize(0);
var yAxisGen = d3.svg.axis()
.scale(yScale)
.orient("left")
.tickValues(_config.keys.yAxis.tickValues.length > 0 ? _config.keys.yAxis.tickValues : 1)
.tickSize(0);
I'd do it after generating the axis:
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," +height + ")")
.call(_config.xAxisGen)
.selectAll('.x .tick text') // select all the x tick texts
.call(function(t){
t.each(function(d){ // for each one
var self = d3.select(this);
var s = self.text().split(' '); // get the text and split it
self.text(''); // clear it out
self.append("tspan") // insert two tspans
.attr("x", 0)
.attr("dy",".8em")
.text(s[0]);
self.append("tspan")
.attr("x", 0)
.attr("dy",".8em")
.text(s[1]);
})
});
Updated example.
Related
I'm having trouble on this.. I'm working on a line chart using d3.js. I'm having trouble to display the X axis tick text that uses date values ONLY in the data spreadsheet. When I develop the line chart, I saw it automatic generated the date values between the values from the Data spreadsheet. Here is a quick example of the data.
date,close
16-Dec-12,53.98
16-Dec-12,67.00
16-Dec-12,89.70
16-Dec-12,99.00
16-Dec-12,130.28
23-Dec-12,166.70
23-Dec-12,234.98
23-Dec-12,345.44
23-Dec-12,443.34
23-Dec-12,543.70
23-Dec-12,580.13
30-Dec-12,605.23
30-Dec-12,622.77
30-Dec-12,626.20
30-Dec-12,628.44
30-Dec-12,636.23
30-Dec-12,633.68
So in this dataset, it has 3 different date values.
In the D3 line chart, I want to display those only the 3 different date values which are Weeks in the x axis tick text. However, the chart is generated other dates in between those data date values. Example below.
I'm trying to display like this that only display the date values from the Dataset.
I hope this makes sense. Is this possible? I tried to use .tick() but it only display '16-Dec-12' which it confuses me. I'm pretty new into line charts for d3.js =/
here is my snippet code. I hope this helps.
function getExtent(member) {
var extents = [];
dataset.forEach(function(arry){
extents = extents.concat(d3.extent(arry,function(d){return d[member]}));
});
return d3.extent(extents);
}
var xScale = d3.time.scale().domain(getExtent('x')).range([0,width]);
var yScale = d3.scale.linear().domain(getExtent('y')).range([height,0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom');
xAxis.scale(xScale)
.tickFormat(d3.time.format('%b %d'));
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left');
var lineFunc = d3.svg.line()
.x(function(d){return xScale(d.x)})
.y(function(d){return yScale(d.y)})
.interpolate('linear');
var g = svg.append('g')
.attr('width',width)
.attr('height',height)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Use this group for drawing the lines
g.append('g')
.attr('class', 'line-group');
// Axes
g.append('g')
.attr('class', 'usps-multiline axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
.selectAll("text")
.attr("transform", "translate(-40,20) rotate(315)");
g.append('g')
.attr('class', 'usps-multiline axis axis--y')
.call(yAxis);
**Please let me know if you can view the sample pic.
This is the expected behaviour for a time scale. In D3, the axis is automatically generated, you don't have much control on the ticks.
The easiest alternative seems to be passing an array of the dates you have in your data to tickValues:
var axis = d3.axisBottom(scale)
.tickValues(uniqueValues);
Here, uniqueValues is an array with the dates you have in your CSV, filtered to only unique dates (otherwise you'll have several ticks in the same position).
here is the demo with the CSV you shared:
var svg = d3.select("svg");
var csv = `date,close
16-Dec-12,53.98
16-Dec-12,67.00
16-Dec-12,89.70
16-Dec-12,99.00
16-Dec-12,130.28
23-Dec-12,166.70
23-Dec-12,234.98
23-Dec-12,345.44
23-Dec-12,443.34
23-Dec-12,543.70
23-Dec-12,580.13
30-Dec-12,605.23
30-Dec-12,622.77
30-Dec-12,626.20
30-Dec-12,628.44
30-Dec-12,636.23
30-Dec-12,633.68`;
var data = d3.csvParse(csv, function(d) {
d.date = d3.timeParse("%d-%b-%y")(d.date);
return d
});
var uniqueValues = [...new Set(data.map(function(d) {
return d.date.getTime()
}))].map(function(d) {
return new Date(d);
});
var scale = d3.scaleTime()
.range([30, 570])
.domain(d3.extent(data, function(d) {
return d.date
}));
var axis = d3.axisBottom(scale)
.tickValues(uniqueValues);
var gX = svg.append("g")
.attr("transform", "translate(0,50)")
.call(axis);
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="600" height="100"></svg>
PS: I'm using D3 v4 in the demo, but the principle is the same.
If .ticks(3) doesn't work, you can pass a custom function into .ticks to ensure you get the ticks you want.
Here is a fairly comprehensive axis tutorial.
I am using nvd3 stackedgroupchart, Now i have changed single X-Axis column label into multiple.
Also i need to add secondary X-Axis for month names. I am using the graph in following link
http://bl.ocks.org/4629518
Now my graph is showing like,
And i need to add months in X-Axis like this,
Now my X-Axis generated based on below code:
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, width], 0.8);
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(function(d) { return dataset[d].shift; })
.orient("bottom");
Please help to add secondary label for X-Axis,
Thanks in Advance,
Create another axis with preferred scale to map
Use copy function to copy the scale
Add new domain values to the scale
Create a new axis and add the scale
call(axisNew) and change the transform values
var x2 = x0.copy();
x2.domain(["a","b","c","d"]);
var xAxis1 = d3.svg.axis()
.scale(x2)
.tickSize(0)
.orient("bottom");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height+10) + ")")
.call(xAxis1);
So I'm dynamically creating a bunch of simple line charts with D3 and everything is going well, but for some reason charts with ranges that go from 9-10 get inverted and look absolutely terrible/do not function properly.
the first one, the values are upside down
this one is fine...
Here is some code...
var dataRange = d3.extent(quoteObjects, function(d){ return d.close });
var dateRange = d3.extent(quoteObjects, function(d){ return d.date });
// Set chart variables
var vis = d3.select("#"+type),
WIDTH = $('#chart-box').width(),
HEIGHT = $('#'+type).innerHeight(),
MARGINS = {
top: 20,
right: 20,
bottom: 20,
left: 60,
},
// set scales
xScale = d3.time.scale().range([MARGINS.left, WIDTH - MARGINS.right]).domain(dateRange),
yScale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain(dataRange),
// create displayed axis
xAxis = d3.svg.axis()
.scale(xScale)
.tickValues( xScale.ticks(6) )
yAxis = d3.svg.axis()
.scale(yScale)
.tickValues( yScale.ticks(6) )
.orient("left");
console.log("WH", WIDTH, HEIGHT);
if (type == "intraday"){
xAxis.tickFormat(d3.time.format("%H"))
}
// append x axis
vis.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.top) + ")")
.call(xAxis);
// append y axis
vis.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
// create line based on "close" values
var lineGen2 = d3.svg.line()
.x(function(d) {
return xScale(d.date);
})
.y(function(d) {
return yScale(d.close);
})
.interpolate("basis");
// append "close" line
vis.append('svg:path')
.attr('d', lineGen2(quoteObjects))
.attr('stroke', '#931a28')
.attr('stroke-width', 3)
.attr('fill', '#222');
I'm plotting the graphs with "number of clicks" as Y axis and "date" as X axis. Because of the large amount of data, the X axis is jumbled and couldn't display all the dates. I tried to use ticks(d3.time.months, 1) and tickFormat('%b %Y') to cur off some of the ticks. When I run the code, I got "getMonth() is not defined" for my data.
.tsv file:
date count
2013-01-01 4
2013-03-02 5
sample code:
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1, 0);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months, 1)
.tickFormat(d3.time.format('%b %Y'))
d3.tsv("data_Bar_Chart_Paino_Kristen.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.hits; })]);
var temp = height + 30; //+15
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(-8," + temp + ")")
.call(xAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.hits); })
.attr("height", function(d) {return height - y(d.hits); });
}
Is there a way to solve my problem and show ticks properly?
You'll need to tell D3 that your axis is a date. Try this:
//assumes the data is sorted chronologically
var xMin = data[0].dateFieldName;
var xMax = data[data.length-1].dateFieldName;
//set the scale for the x axis
var x = d3.time.scale().domain([xMin, xMax]).range([0, width]);
//straight from your code
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months, 1)
.tickFormat(d3.time.format('%b %Y'))
The trick here is to use the d3.time.scale() method.
If you use the d3.time.scale() you have another problem, you can't use x.rangeBand() when you draw the rect.
Working with one bar chart with d3.js I am unable to align ticks in x axis with bars.
In left and right verges the ticks are ok, but not in the middle.
Here is the code:
var formatDate = d3.time.format("%e %b");
var height = 325;
var xTimeScale = d3.time.scale()
.domain([new Date(data[0].date), d3.time.day.offset(new Date(data[data.length - 2].date), 1)])
.range([30, width]);
var xAxis = d3.svg.axis()
.scale(xTimeScale)
.orient("bottom")
.ticks(d3.time.days, .1)
.tickFormat(formatDate);
chart.append("g")
.attr("class", "xaxis axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.selectAll(".xaxis text")
.attr("transform", function(d) {return "translate(" + this.getBBox().height * -2 + "," + this.getBBox().height + ")rotate(-45)";});
What am I missing?
Thanks in advance.
Update: Here is the jsFiddle updated with chrtan suggestions.
My problem now is to align text with the center of bar and not in left.
From the looks of your graph, you lost a bar's worth of space. If all the bars are supposed to be left-aligned against a tick, that final bar you have should be to the right of the January 31st tick.
You might need to add the February 1st tick by perhaps changing the [data.length - 2] to [data.length - 1] in the domain() for your xTimeScale.
Then for display purposes, you could probably remove the last tick axis text with:
d3.select(chart.selectAll(".xaxis text")[0].pop()).remove();
The inner selectAll should get the array containing your xAxis tick texts and then pop the very last tick. This last tick should then be removed by the outer select.
An example with an auto time ticks with d3.js
// set domain for axis
var x_domain = d3.extent(data, function(d) { return new Date(d.date); });
//format date
var date_format = d3.time.format('%Y %B');
var vis = d3.select("#graph")
.append("svg:svg")
.attr('class', 'chart')
.attr("width", width)
.attr("height", height);
var yScale = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.data; })]).nice()
.range([height - padding, padding]);
var xScale = d3.time.scale()
.domain(x_domain)
.range([padding, width - padding]);
// define the y axis
var yAxis = d3.svg.axis()
.orient("left")
.scale(yScale);
// define the x axis
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(xScale)
.tickFormat(date_format);
// draw y axis with labels and move in from the size by the amount of padding
vis.append("g")
.attr("class", "axis")
.attr("transform", "translate("+padding+",0)")
.call(yAxis);
// draw x axis with labels and move to the bottom of the chart area
vis.append("g")
.attr("class", "xaxis axis")
.attr("transform", "translate(0," + (height - padding) + ")")
.call(xAxis);
// and set data in graph...
a great example : http://bl.ocks.org/phoebebright/3061203