How to set unequal intervals on Y axis in D3.js? - javascript

I am trying to create a chart that will have a custom tick range. Having an issue on how to set up the axis though, tried using logscale too but it didn't work properly. Any help is appreciated, attaching a pic for reference.
How I want the axis to be
var y = d3.scaleLinear()
.domain(d3.extent(props.dailyDataAll, function (d) { return d.confirmed }))
.range([height, 0])

I know this really does not answer your question, but I think it may be just a common XY problem.
As such I would suggest instead of struggling to plot ticks properly, you could simply replace Y values with Math.log10(Y) which would make ticks work exactly as you wanted (100 being 2 on the Y scale, 1000 being 3, 10000 being 4 and so on, effectively one tick per order of magnitude just like in your requirements)

Related

d3.js ticks for axis displayed wrong

I have a problem regarding ticks for the axis label using d3.js
This is my code:
const svg = d3.select('svg').attr('width', 1000).attr('height', 600);
const layer = svg.append('g').attr('transform', `translate(30, 30)`);
const scale = d3.scaleLinear().range([0, 750]).domain([0, 350]);
const axis = d3.axisTop(scale).ticks(14);
layer.call(axis);
So what i want to do is, have 14 labels regarding the data. So by dividing 350/14 a step should be of size 25.
If you take a look at the result, the ticks are rounded somehow to 20, which I do not know why.
Does anyone know what d3 is doing here? What am I doing wrong?
Thanks in advance!
Output
The number of ticks you pass is only a hint to d3. According to the observable documentation here:
For linear and power scales, pass axis.ticks the desired tick count. This is just a hint: these scales only generate ticks at 1-, 2-, and 5-multiples of powers of ten, so the actual number of ticks may be different.
If you want to use labels on the axis, you should consider using the scaleBand which is demonstrated here

D3 scatter chart dynamic axis positions

Im using this scatter chart example to achieve dynamic axis changing on data change.
I want to achieve two things:
1 - the X and Y axis intercept at 0
2 - both axis intercept in the middle of the chart to accommodate negative numbers better
This is where Ive got to : Ive managed to get the Y axis into the middle but once the data is changing the X and Y axis are not aligning properly. Could anyone suggest a fix in my update function ? My edited example is here.
Thank you
To make axis intercept at 0 in the middle of the chart with linear scales, you should define symmetric domains:
var ds = randomData(200);
var xmax = d3.max(ds, function (d) { return Math.abs(d.x); });
var ymax = d3.max(ds, function (d) { return Math.abs(d.y); });
x.domain([-xmax, xmax]).nice();
y.domain([-ymax, ymax]).nice();
Demo

How to make axis "smart" and "flexible" so that the starting/end points on axis are sensible in D3 V4?

I have the following Line Graph (CodePen) and as you can see, the Y-Axis isn't very smart. It begins from 0 when it should begin from somewhere near 2200.
My guess was that this would be to do with scales:
// set the ranges
var x = d3.scalePoint().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
But I tried all sorts to get this to behave the "smart" and "flexible" way and couldn't.
How can I make the axis smarter? So if tomorrow I feed the chart a different set of data that starts at 150, the code is smart enough to draw relevant axis values.
If you don't want the Y axis starting at 0, you shouldn't set the lower value in the domain to zero, as you did:
y.domain([0, d3.max(result, function(d) {
return d.consumption;
})]);
If you want the Y axis to start next to the minimum value, you should use:
y.domain([d3.min(result, function(d) {
return d.consumption; })*0.975,
d3.max(result, function(d) {
return d.consumption; })*1.025
]);
Here, the numbers 0.975 and 1.025 are just values to make the axis going a little bit below and after the minimum and maximum values, respectively. You can change them or remove them.
Here is your updated CodePen: http://codepen.io/anon/pen/EgOBvO?editors=0010
PS: while in a bar chart it's highly recommended to always use a zero baseline for the Y axis, there is no problem using a non-zero baseline for the Y axis in a time series (line chart).

Automatic Date Labeling for NVD3 Graphs

I want the date labels to automatically be calculated, appear, and disappear when I change the focus range so that they don't overlap.
I am using a MultiBar graph with a focus chart with the default ordinal scale for nv.models.multiBar(). When I use .ticks(availableWidth / 100 ) on the xAxis, it seems to generate a tick label for EVERY date, or at least a very large number of them:
On nv.models.lineWithFocusChart(), the labels are automatically reduced to fit in a space. This could be because it uses the scale for nv.models.scatter() which is a d3.scale.linear(), but I'm not sure. I tried creating my own scale with the following:
x = d3.scale.ordinal() //as well as x = d3.scale.linear()
.domain(d3.extent(data.map(function(d) {
return d.values.map(function(d,i) {
var X = getX(d,i);
return X.getTime();
});
})))
.range([0, availableWidth]);
I get the following for an ordinal scale:
and no labels for a linear scale. Will this approach work? If so, what am I doing wrong?
On nv.models.multiBarChart(), there is a .reduceXTicks(BOOLEAN) option but this only applies to multiBarChart and there doesn't seem to be an easy way to add it to nv.models.multiBar(). Can I somehow use this?
If there is anything I haven't tried please let me know. I don't want to calculate the labels myself and specify them using .tickValues()
The solution was in fact to use d3.scale.linear() for the x axis. What I tried above didn't work because I was specifying the whole domain of the context chart, when I needed to specify the min and max of the current selection.
In chart(selection) {...}, I put
x = d3.scale.linear()
.range([0, availableWidth]);
and in onBrush(), I put x.domain([new Date(extent[0]), new Date(extent[1])]);, where extent contains the min and max dates of the selection in milliseconds.

Brushing on ordinal data does not work

I really like this graph and its functionality and it is perfect for what I want/need. The only thing I need to change is I need it to allow ordinal data on the y-axis and I cannot seem to get that to work (I am a beginner).
When I change the y scale from linear to ordinal:
yscale[k] = d3.scale.linear()
.domain(d3.extent(data, function(d) { return +d[k]; }))
.range([h, 0]));
to
yscale[k] = d3.scale.ordinal().rangePoints([h, 0]),
yscale[k].domain(data.map(function(d) { return d[k]; })))
Brushing still shows up and works by itself but it does not filter leaving the selected lines. No lines show up unless I move it to the very top of the axis then, all or mostly all show up. When I stepped through the code with firebug it looked like it was just not getting the lines that were in the brush area but all(?)... and I can't seem to figure out. :(
If anyone could help out with this (especially all the places I have to change and how), I would love to get this working and learn what I am doing wrong :-\
Brushing an ordinal axis returns the pixels, while brushing a quantitative axis returns the domain.
https://github.com/mbostock/d3/wiki/SVG-Controls#wiki-brush_x
The scale is typically defined as a
quantitative scale, in which case the extent is in data space from the
scale's domain; however, it may instead be defined as an ordinal
scale, where the extent is in pixel space from the scale's range
extent.
My guess is that you need to work backwards and translate the pixels to the domain values. I found this question because I'm trying to do the same thing. If I figure it out, I'll let you know.
EDIT: Here's an awesome example to get you started.
http://philau.willbowman.com/2012/digitalInnovation/DevelopmentReferences/LIBS/d3JS/examples/brush/brush-ordinal.html
function brushmove() {
var s = d3.event.target.extent();
symbol.classed("selected", function(d) { return s[0] <= (d = x(d)) && d <= s[1]; });
}
He grabs the selection extent (in pixels), then selects all of the series elements and determines whether they lie within the extent. You can filter elements based on that, and return data keys or what have you to add to your filters.
There is an example of an ordinal scale with brushing here:
http://bl.ocks.org/chrisbrich/4173587
The basic idea is as #gumballhead suggests, you are responsible for projecting the pixel values back onto the input domain. The relevant snippet from the example is:
brushed = function(){var selected = yScale.domain().filter(function(d){return (brush.extent()[0] <= yScale(d)) && (yScale(d) <= brush.extent()[1])});
d3.select(".selected").text(selected.join(","));}

Categories

Resources