How to graph dates on X axis while skipping weekends d3.js? - javascript

I am trying to plot stock data with a d3 line Chart. It has ugly spaces for weekends because there is no data available for weekends. What is the best way to make the X axis chart only the dates that I have data for?
const x = d3
.scaleTime()
.domain(d3.extent(data, (d) => d.datetime))
.range([0, width]);
d3 newbie experiencing a heavy learning curve at the moment.

For anyone who may have the same question:
const x = d3
.scaleBand()
.domain(data.map((d) => d.datetime))
.range([0, width]);
Band scale graphs only the dates you have data for.

Related

Non continuous date domain using scaleTime()

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?

Plotting heatmap with available attributes instead of range D3 React

I'm working on a heatmap which basically plots the graph between taxIDs and KeywordNames from an external JSON. While I'm able to plot the values I see many blank spaces on the graph and clueless how I can plot them with the available data.
Here's the link to codesandbox: https://codesandbox.io/s/40mnzk9xv4
On the X-Axis I'm plotting the TaxIDs which are being calculated within the given range. I did try using the function rangeBands() but I get an error everytime.
Its the similar case with Y-Axis where I'am plotting the keywordIDs which are also being calculated within a range. I'm trying to print all the KeywordNames on Y axis and all taxIDs on the X-Axis and plot their corresponding spectracount on graph.
Please help me where have I gone wrong.
The output I'm looking for is something similar to this: https://bl.ocks.org/Bl3f/cdb5ad854b376765fa99
Thank you.
Some things to help you get you one your way:
First, your scales should use scaleBand(), not scaleLinear(), as they have discrete domains (i.e. categories of something, rather than continuous)
Second, your scale domains is taking every value of taxId and keywordName in your data as a possible value. But some values are repeated more than once. You need to be filtering them so you only have unique values. So your scale code should be:
const xValues = d3.set(data.map(d => d.taxId)).values();
const yValues = d3.set(data.map(d => d.keywordName)).values();
const xScale = d3.scaleBand()
.range([0, width])
.domain(xValues); //X-Axis
const yScale = d3.scaleBand()
.range([0, height])
.domain(yValues); //Y-Axis
Finally, your code that places the heatmap tiles needs to be calling the scale functions so it works out the position of each rect correctly:
chart.selectAll('g')
.data(data).enter().append('g')
.append('rect')
.attr('x', d => { return xScale(d.taxId) })
.attr('y', d => { return yScale(d.keywordName) })
That should get you most of the way there. You'll want to also reduce cellSize and remove the tickFormat calls on the axes as they are trying to convert your text labels to numbers.

d3.js chart flowing out of the grid

I'm trying to understand d3.js and experienced an issure related to drawing chart using d3.js. some of the data comes fine within the grid, but for some data it goes out of bound.Below is how my graph looks:
below is the js:
var svg = self.graph.selectAll(".series").data([data]);
svg.enter().append("svg:path");
//hardcoded values just for this example.
self.x = d3.time.scale()
.range([0, 881]);
self.y = d3.scale.linear()
.domain([0, 2), d3.round(36,2)])
.range([210, 15]);
How can I set a height width to the chart in order to avoid it going from out of bound?any ideas ? Thanks!!

Scaling d3's histogram x domain causes "Invalid negative value for <rect> attribute" errors

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.

D3 time and date histogram

I'm attempting to make a histogram using primarily time and date data, provided in a json file (along with other info) in this format: 2014-03-01 00:18:00. I've looked at http://bl.ocks.org/mbostock/3048450 as an example, but I haven't managed to crack it. The key part seems to be this:
var data = d3.layout.histogram()
.bins(x.ticks(20))
(dataset.timestamp);
When I view my code in the browser it gives "TypeError: data is undefined", and refers to d3.v3.js line 5878.
Assuming I fix that error, the next place it may stumble is the axis formatting:
var formatDate = d3.time.format("%y-%m-%d %h:%m:%s");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(formatDate);
Will the format syntax correspond correctly to my time and date format?
I don't think that the histogram layout accepts non-numeric input (I could be wrong). One option would be to convert the dates into the number of milliseconds since 1970 by parsing and using getTime():
var formatDate = d3.time.format("%Y-%m-%d %H:%M:%S");
var data = d3.layout.histogram()
.bins(x.ticks(20))
.value(function(d) {return formatDate.parse(d.timestamp).getTime()})
(dataset);
You'll need to make sure that the x scale (as in x.ticks(20)) has a domain based on the millisecond data.
So I figured it out, thanks to the helpful answers here. First off, the json data is acquired:
d3.json("inkmarch.json", function(error, json) {
if (error) return console.warn(error);
dataset = json;
Then I specify a formatting variable specific to my date formatting:
var formatDate = d3.time.format("%Y-%m-%d %H:%M:%S");
I map the re-formatted dates to another var:
mappedSet = (dataset.map(function(d) {return formatDate.parse(d.timestamp).getTime()}))
And finally I can make a proper histogram dataset.
var data = d3.layout.histogram()
.bins(x.ticks(31))
.value(function(d) {return formatDate.parse(d.timestamp).getTime()})
(dataset);
(31 ticks since this is monthly data from March). I did away with the d3.time.scale since they were integers anyway, so my scales look like such:
var x = d3.scale.linear()
.domain([d3.min(mappedSet), d3.max(mappedSet)])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.y; })])
.range([height, 0]);
(Still not sure what the function in the y domain does). The svg and bar vars look exactly like the example I linked in my question post, as do the 'rect' and 'text' bars (except I changed width of rect and x position of the text to fixed values since the example formulas were giving hideous negative values).
Thanks again for the help, I'm sure I'll have more d3 questions in the near future.

Categories