I am making a scatterplot using D3.js and I am unable to apply a proper time scale to the X axis. All my data is within the same year (2016), in a 3 months span.
I usually do this in a different way, and I want to learn what I am doing wrong in this one, which has a different code structure as I usually use in D3.
Here is the code in JsBin
This is X axis code:
var xValue = function(d) {
return d.datadois;
},
xScale = d3.time.scale(),
xMap = function(d) {
return xScale(xValue(d));
},
xAxis = d3.svg.axis().scale(xScale).orient("bottom").ticks(10, ".f");
Wonder how can I apply the time scale properly, to render the months in my data.
Related
I'm using D3 v4 to build a very simple chart. I'm using a time scale for the x axis. I know that D3 maintains control over the number of ticks shown, when using a time scale. But that doesn't seem to explain the behavior here.
The codepen (linked below) shows an RTC of the behavior. I have 5 dates, defined very simply as 2018-10-10, 2018-10-11, and so on. I'm using d3.scaleTime() and d3.axisBottom(), nothing fancy.
No matter what I do, the last date is never included in the tick marks. Why is this happening?
Here's a link to the codepen
There are certain challenges while using timescale in d3. Here is the working code. You can update the tick format and height/width accordingly-
var svg = d3.select('#graph')
.attr('width', '400px')
.attr('height', '200px')
var data = [
new Date('2018-10-10'),
new Date('2018-10-11'),
new Date('2018-10-12'),
new Date('2018-10-13'),
new Date('2018-10-14')
]
var x = d3.scaleTime().range([50, 350])
x.domain(d3.extent(data))
svg.append('g')
.attr('class', 'x-axis')
.call(d3.axisBottom(x) .tickValues(data.map(function(d){return d}))
.tickFormat(d3.timeFormat("%Y-%m-%d")));
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.
I'm trying to recreate the following in D3
And I've got the following so far: http://codepen.io/jpezninjo/pen/dNwmVK
I looked it up and the best I could find was this answer: Show every other tick label on d3 time axis?, but I'm not using a class to create each column label. I think the following two lines are the ones that control my labels, but I'm not sure how to go about this.
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
x.domain(data.map(function(d) { return d.key; }));
I'm also trying to figure out how I can put some padding on the left and right of the bars
At least two possible ways:
Make your X axis a time axis, and use d3.timeDay.every(2) to specify every 2nd day. That approach is shown here: http://codepen.io/anon/pen/YNdaRB.
Key part: var axisBottom = d3.axisBottom(x).ticks(d3.timeDay).tickArguments([d3.timeDay.every(2)]);.
To make this work, I also had to (a) make d.key equal to the result from parseDate instead of the formatted date string, (b) hard-code a width for the bars instead of using x.bandwidth(), and (c) translate the x axis by width/2 px to center it under the bars (line 94). Might be nicer ways to do (b) and (c) but I mainly wanted to show d3.timeDay.every(2)'s ability (docs).
Use your current approach but apply a style to every 2nd tick. d3 adds some classes automatically so selecting the ticks is easy. This is what they described in the post you linked to. http://codepen.io/anon/pen/qRLogy?editors=1010
Key part: d3.selectAll(".tick text").style("display", function (d, i) { return i % 2 ? "none" : "initial" })
I've been creating some charts to plot intra-day data from financial results. Often this is a value generated every ten minutes. This varies but its a good example. Therefore there are large periods of time I won't get information, eg when markets are close over weekend and between 5:00pm in the evening and 9:00pm in the morning. I've tried created a custom time scale for the x-axis but in the end the best result is to just use an ordinal scale. it works well and gives the result that I and the people reading the chart want ie, no gaps in the line plot and evenly space data points. (Its the convention)
My question is how do I know plot custom ticks on this xAxis in the correct place, given that I generate them in an array called ticks.major. The example below shows how I generate the axis and there are the correct number of days. But they all are being plotted at the beginning of the graph. Any help appreciated thanks.
var xScale = d3.scale.ordinal()
//var xScale = scaleWeekday()...custom timescale no longer used
.domain(xDomain)
.rangeBands([0,(plotWidth-yLabelOffset)])
var xAxis = d3.svg.axis()
.scale(xScale)
//.tickValues(ticks.major)//used to create tick d3 time scale axis
.tickFormat(function (d,i) {
return ticks.major[i]
})
.tickSize(yOffset/2)
.orient("bottom");
var xLabel=plot.append("g")
.attr("class",media+"xAxis")
.attr("transform",function(){
if(yAlign=="right") {
return "translate("+(margin.left)+","+(plotHeight+margin.top)+")"
}
else {return "translate("+(margin.left+yLabelOffset)+","+(plotHeight+margin.top)+")"}
})
.call(xAxis);
it looks like this:
I think the mistake I'm making is that by using the tick.major array its applying the tick value to the first 12 dates that are passed bacause thats all thats in the tick.major array. Then because there are no more dates in the tick.majore array it has no labels for any further date in the series. Its better to apply a test in the tickFormetter to see if the current day at a particular datapoint is different from the day at the previous datapoint. Then return a tick. like this
.tickFormat(function (d,i) {
if(i>0) {
var day=d.getDay()
var yesterday=data[i-1].date.getDay()
console.log(day,yesterday)
if(day!=yesterday) {
return d
}
}
})
The returned d needs a bit of date formatting to make it readable
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.