consider the example in the link Labeling the axis with alphanumeric characters. In this example the gridlines are dynamic that is the number of gridlines increases or decreases according to co-ordinate values. How to make this static i.e. make the chart as it is in the example independent of co-ordinate values ?
NOTE :- By changing the last co-ordinate value [17,16] the entire chart is either compressed or expanded depending on co-ordinate value.
The simplest way to achieve this is to fix the upper range of the domain. As a simple example, if you know your values in x and y will not exceed 30, you can do this:
var x = d3.scale.linear()
.domain([0,30])
.range([0, width]);
var y = d3.scale.linear()
.domain([0,30])
.range([0, height]);
Related
Preface: I've gone through all other stackoverflow questions relating to this problem I could find, which are outdated or recommend using a ordinal scale instead. Including this one: D3 Non-Continuous Dates Domain Gives Gaps on X-Axis
which describes my problem as well.
Goal: I am trying to create a candlestick chart with zoom in/zoom out and panning. Currently the chart is working as intended, minus the x-axis (which contains dates/times of each candlestick).
I want the x axis to function like it does with scaleTime(), where it shows years/months, and when you zoom in it shows the date/time, depending on the time interval. I know timeScale() is continuous, but I need the functionality of the zooming in and out.
Problem: Gaps are included in the x-axis since when I set the domain for the x-scale and x-band, I am using the minimum and maximum dates in the dataset (named 'prices').
Code (d3js v7):
Works but x-scale is not dates, but decimal values from -1 to dates.length:
var xScale = d3.scaleTime()
.domain([-1, dates.length])
.rangeRound([0, w]);
Works x-scale is dates, but includes gaps for weekends and holidays:
var xScale = d3.scaleTime()
.domain([xmin, xmax])
.rangeRound([0, w]);
Doesn't work, x-scale is blank, coordinates are messed up:
var xScale = d3.scaleTime()
.domain(prices.map(r => r.t))
.rangeRound([0, w]);
I calculate the x coordinates for my candle bodies for (map) and [xmin,xmax] as:
.attr('x', (d, i) => xScale(d.t) - xBand.bandwidth())
or for domain([-1, dates.length])
.attr('x', (d, i) => xScale(i) - xBand.bandwidth())
xBand is declared as:
let xBand = d3.scaleBand()
.domain(d3.range(-1, dates.length))
.range([0, w])
.padding(0.2);
Another solution I can think of is to somehow convert the demical values from the domain in the first xScale example to function like the scaleTime() scale using tickFormat(), but I'm not sure how I would go about doing that. I have already converted them from decimal to dates using .tickFormat(prices.map(d => new Date(d.t))); but don't know where to go from there.
Questions: Can you pass an array of values as the domain? Or is it just a min and max value, since domain is defined as boundaries within which your data lies?
Am I passing the array mapping correctly? Is this allowed?
Finally, how can I set my domain to the array of dates I have without including gaps on the chart?
I have a scatterplot that uses constraint relaxation to de-conflict the labels for the points that it graphs (Plunker here). The problem is that, when I relax the constraints, this causes collisions between the point labels and the x-axis labels. The axes are generated using d3.extent and d3.scale.linear.
I've tried to de-conflict the point labels and the x-axis by extending the length of the y-axis, but the closest I've come to achieving this is by changing the original value of 0 to 30 in the following stanza:
var yext = d3.extent(data, d => d[1]);
var sy = d3.scale.linear()
.domain(yext)
.range([height, 30]) // flip y-axis
.nice();
The result is less than ideal, leaving an awkward gap instead of an intersection between the x and y axes:
What I want to achieve is something like this:
(Except I want to achieve this through code, rather than Photoshop).
Can anyone demonstrate a solution? (Plunker here)
Why don't you add a padding in the domain? Like:
.domain([yext[0] * 0.95, yext[1] * 1.05])
//less here---------^-- more here----^
Here is the plunker with that solution: http://plnkr.co/edit/rKArjn7DwQa9g1X5CaNW?p=preview
I'm trying to create a simple scatter plot in d3 (similar to this one from matplotlib):
I use extent() to set the scale's input domain range.
xScale.domain(d3.extent(xvalues));
Using this approach results in some dots overlapping axises in d3 plot:
How to avoid axis overlapping and make a margin similar to the matplotlib's plot?
Input values vary, so simple increment / decrement of the extent() output doesn't look like a general solution.
In general, the best way of handling this is to call the scale's .nice() function, which will round the ends of the domain of the scale to nice values. In your particular case, this doesn't work, as the values are "nice" already.
In this case I would compute the extent of the domain and extend it by a fraction of that. For example:
var padding = (xScale.domain()[1] - xScale.domain()[0]) / 10;
xScale.domain([xScale.domain()[0] - padding, xScale.domain()[1] + padding]).nice();
In your matplotlib image, the dots are not overlapping and the x scale has negative value.
In d3:
var xScale = d3.scale.linear()
.domain([
d3.min(data, function(d) {
return d.val;
})-10, //so the domain is wider enough for the zero value
d3.max(data, function(d) {
return d.val;
}),
])
.range([height , 0])
I created a clock with d3 by making a bar chart updates as each second passes.
Typically I would set my yScale like so:
var yScale = d3.scale.linear()
.domain(maximumTime)
.range([svgHeight, 0]);
but this will leave an a blank bar at each new minute, hour, and day. To solve for this I've set the to go to -1 so something is always displayed, even when the value is zero.
var yScale = d3.scale.linear()
.domain(maximumTime)
.range([svgHeight, -1]);
Is there a more graceful way that I can display zero values?
Full example in a CodePen: http://codepen.io/agconti/pen/vEWZXb
I'm trying to display a histogram using D3.
I started with the official example here and tried to change the scale of the x domain.
However, if I change the scale of the x domain, I get errors on the width of the individual histogram buckets.
The code in the example works (jsfiddle):
var x = d3.scale.linear()
.domain([0, 1])
.range([0, width]);
But this does not (jsfiddle):
var x = d3.scale.linear()
.domain([0.2, 1])
.range([0, width]);
Others have mentioned that, in order to zoom the x axis, you should use this:
var x = d3.scale.linear()
.domain(d3.extent(data))
.range([0, width]);
However, that's not possible since data has not been created yet, because data requires x:
var x = d3.scale.linear()
.domain([60, 95])
.range([0, width]);
// Generate a histogram using twenty uniformly-spaced bins.
var data = d3.layout.histogram()
.bins(x.ticks(7))
(values);
So how can I use data to create x if x is needed to create data?
Note that scaling the large side of the histogram does work:
var x = d3.scale.linear()
.domain([0, Number(d3.max(values))])
.range([0, width]);
However, if the small side is anything but zero, things break:
var x = d3.scale.linear()
.domain([Number(d3.min(values)), Number(d3.max(values))])
.range([0, width]);
The way you're computing the width of the bars is incorrect for your particular use case; in particular it results in negative widths (as the error message indicates). You need to take the width of the range and divide it by the number of items (minus a small number if you want gaps):
.attr("width", (x.range()[1] - x.range()[0]) / data.length - 2)
Complete demo here.