D3.js y-Axis time scale - javascript

is it possible to have a line chart with a time-scaled y-Axis in D3.js?
I found the documentation:
https://github.com/mbostock/d3/wiki/Time-Scales
and there doesn't seem to be a limitation for the Y-Axis.
Do you have any examples?

Its pretty much the same as using a linear scale:
var svg = d3.select('#your_svg_id').select("svg")
.attr('width', 300)
.attr('height', 300)
.append("g");
var yScale = d3.time.scale()
.domain([new Date(1980, 0, 1), new Date() ])
.range([100, 0]);
// Domian being the from and to date and range the from to positioning of the axis
svg.append("svg:g")
.attr("class", "y axis")
.call(yAxisGen);

Related

d3 : Y Axis intervals not getting set by ticks

I want to set Y Axis by 0-100 percent in the intervals of 0-25-50-75-100.
For that I am setting ticks to 4.
But I can see Y axis in the intervals of 0-20-40-60-80-100
How can I set Y axis in the intervals of 0-25-50-75-100 ??
I have below code -
const extent=[0,100];
const yScale = scaleLinear().domain(extent).range([height, 0]);
const x0Scale = scaleBand()
.domain(data.map((d) => d.month))
.range([0, width])
.padding(0.46);
const x1Scale = scaleBand()
// .domain(data.map((d) => d.type))
.rangeRound([0, x0Scale.bandwidth()])
.padding(0.22);
const xAix = axisBottom(x0Scale);
const yAix = axisLeft(yScale);
svg
.select(".x-axis")
.attr("transform", `translate(${xMargin}, ${height + yMargin})`)
.style("font-weight", "bold")
.style("font-size", "0.700rem")
.call(xAix);
svg
.select(".y-axis")
.attr("transform", `translate(${0 + xMargin}, ${yMargin} )`)
.style("font-weight", "bold")
.style("font-size", "0.725rem")
.call(yAix.ticks(4).tickSize(-width).tickFormat(function(d){return d + "%"}));
d3 takes priority for some of the tick functions , you can do force tick Values by tickValues(d3.range(minValue, maxValue, noOfSteps)); . In your case
yAix.tickSize(-width).tickValues(d3.range(0,100,4).tickFormat(function(d){return d + "%"})
or use tickValues(d3.range(d3.min(dataarray),d3.max(dataarray),noOfticks))
Use tickValues instead of ticks.
svg
.select(".y-axis")
.attr("transform", `translate(${0 + xMargin}, ${yMargin} )`)
.style("font-weight", "bold")
.style("font-size", "0.725rem")
.call(yAix
.ticksValues([0, 25, 50, 75, 100])
.tickSize(-width)
.tickFormat(function(d){return d + "%"}));
ticks is used as a hint only - d3 might display a few more or less ticks, depending on the scale and the available screen space.
tickValues allows you to select the exact values for which to display ticks.

Grid lines in d3 js don't match the axes

I am trying to make a multi-line chart with d3.js in react. The plot looks fine and comes up well, but the gridlines are not aligned sometimes. It is very random, and sometimes some graphs have aligned gridlines, some don't.
This is how some of them look:
I have this code for my gridlines:
svg
.append('g')
.attr('class', 'grid')
.attr('transform', `translate(0,${height})`)
.call(
d3.axisBottom(xScale)
.tickSize(-height)
.tickFormat(() => ""),
);
svg
.append('g')
.attr('class', 'grid')
.call(
d3.axisLeft(yScale)
.tickSize(-width)
.tickFormat(() => ""),
);
I followed this example: https://betterprogramming.pub/react-d3-plotting-a-line-chart-with-tooltips-ed41a4c31f4f
Any help on how I can align those lines perfectly would be appreciated.
You may consider niceing your y-scale so that minima and maxima of your data sets are rounded down/ up such that the ticks are equally spaced.
In your tutorial this bit of code:
const yScale = d3
.scaleLinear()
.range([height, 0])
.domain([0, yMaxValue]);
Can become:
const yScale = d3
.scaleLinear()
.range([height, 0])
.domain([0, yMaxValue])
.nice(); // <--------------------------- here
Here's a basic example of using nice on an x-scale where the first example is 'not nice' and the second is 'nice'.
// note gaps of 10 between data points
// apart from first and last where gap is different
const data = [3, 4, 14, 24, 34, 44, 47];
// svg
const margin = 20;
const width = 360;
const height = 140;
const svg = d3.select("body")
.append("svg")
.attr("width", width + margin + margin)
.attr("height", height + margin + margin);
// scale without 'nice'
const xScale1 = d3.scaleLinear()
.range([0, width])
.domain(d3.extent(data));
// scale with nice
const xScale2 = d3.scaleLinear()
.range([0, width])
.domain(d3.extent(data))
.nice();
// plot axes with both scales for comparison
// not 'nice'
svg.append("g")
.attr("transform", `translate(${margin},${margin})`)
.call(d3.axisBottom(xScale1));
// 'nice'
svg.append("g")
.attr("transform", `translate(${margin},${margin + 50})`)
.call(d3.axisBottom(xScale2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

How to reformat axis labels?

I've begun learning D3, and many tutorials are written for v3. I read through a lot of v4's documentation, but I can't wrap my mind around how to format my axis labels. Right now I have a visualization that looks like this:
The months overlapping is not ideal. I know I can use d3's timeFormat and use "%b" to use an abbreviated month. But I'm not sure how syntactically it fits in. My code for the bottom axis is:
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.ticks(d3.timeMonth.every(4)));
I would've expected it to be something like
format = d3.time.format("%b")
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.ticks(format(d3.timeMonth.every(4))));
But that doesn't work. I know this is probably very simple, but I'm not getting it.
You should use tickFormat instead of ticks:
.tickFormat(d3.timeFormat("%b"))
Here is a demo:
var svg = d3.select("svg")
var scale = d3.scaleTime()
.range([20, 480])
.domain([new Date(), new Date().setYear(new Date().getFullYear() + 1)]);
var axis = d3.axisBottom(scale)
.tickFormat(d3.timeFormat("%b"))
.ticks(d3.timeMonth);
svg.append("g")
.attr("transform", "translate(0,50)")
.call(axis)
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
EDIT: For showing the year instead of January:
var svg = d3.select("svg")
var scale = d3.scaleTime()
.range([20, 480])
.domain([new Date(), new Date().setYear(new Date().getFullYear() + 1)]);
var axis = d3.axisBottom(scale)
.tickFormat(function(d) {
return d3.timeFormat("%b")(d) === "Jan" ?
d3.timeFormat("%Y")(d) : d3.timeFormat("%b")(d);
})
.ticks(d3.timeMonth);
svg.append("g")
.attr("transform", "translate(0,50)")
.call(axis)
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>

scaleTime minimum tick value

Using d3 I have an xAxis defined as
var xScale = d3.scaleTime()
.domain([fromDate, toDate])
.range([0, width]);
var xAxis = d3.axisBottom(xScale);
function customXAxis(g) {
g.call(xAxis);
g.select(".domain").remove();
g.selectAll(".tick line").attr("stroke", "white");
g.selectAll(".tick text").attr("fill", "white");
}
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(customXAxis);
All is working very well indeed.
However, I would like the scale to show ticks at a minimum of a day. As it stands if fromData and toDate are only a few days difference it show ticks....
Mon-10___12PM___Tue-11___12PM___Wed-13___12PM___Thu-14___12PM
How can I get it to not show the time of day values?
Any help appreciated.
You can set the intervals in the axis generator:
Constructs a new custom interval given the specified floor and offset functions and an optional count function.
For instance, this is your code as it is (the domain here has just 3 days):
var svg = d3.select("svg");
var xScale = d3.scaleTime()
.domain([new Date("January 1, 2017 00:00:00"), new Date("January 4, 2017 00:00:00")])
.range([20, 480]);
var xAxis = d3.axisBottom(xScale);
svg.append("g")
.attr("transform", "translate(0,100)")
.call(xAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
Now the same code, with d3.timeDay:
var svg = d3.select("svg");
var xScale = d3.scaleTime()
.domain([new Date("January 1, 2017 00:00:00"), new Date("January 4, 2017 00:00:00")])
.range([20, 480]);
var xAxis = d3.axisBottom(xScale)
.ticks(d3.timeDay)
.tickFormat(d=>d3.timeFormat("%a %d")(d));
svg.append("g")
.attr("transform", "translate(0,100)")
.call(xAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
PS: the tickFormat is just to change the first tick, don't pay attention to it.

D3 x-axis for each day of the week

I have a JSON array like the following:
[
{
"day": "Monday",
"sales": 242
},
{
"day": "Tuesday",
"sales": 256
},
...
]
This data covers one week, so there is an object for each day Monday through Sunday.
I have built a bar graph with D3 with a bar for each day of the week. I am now attempting to add an x-axis with a tick label for each day.
I've done the following to set up my y-axis:
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.sales;
})])
.range([height, 0]);
var yAxis = d3.axisLeft(yScale)
.ticks(5);
svg.append("g")
.call(yAxis);
This works great, but for some reason, I am stumped on how to setup my x-axis with each day of the week under the corresponding bar. Note: I am using D3 version 4.
Once your days are just strings, you can use scaleBand instead of scaleTime:
var xScale = d3.scaleBand()
.domain(data.map(function(d){ return d.day}))
.range([0, width])//you can use rangeRound instead
.paddingInner(someValue);//the value of the inner padding, if any.
Then, set the x axis accordingly:
var xAxis = d3.axisBottom(xScale);
Check this snippet:
var width = 550, height = 200;
var data = [{day:"Monday"},
{day:"Tuesday"},
{day:"Wednesday"},
{day:"Thursday"},
{day:"Friday"},
{day:"Saturday"},
{day:"Sunday"}
];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var xScale = d3.scaleBand()
.domain(data.map(function(d){ return d.day}))
.range([0, width*0.95])
var xAxis = d3.axisBottom(xScale)
.ticks(7);
svg.append("g")
.attr("transform", "translate(0,100)")
.call(xAxis);
text { font-size: 12px;}
<script src="https://d3js.org/d3.v4.min.js"></script>

Categories