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" })
Related
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'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
Good afternoon.
I am using this D3 bar chart example with my data (that is in the same format whereof in the
jsfiddle example).
The chart works well but i have some problems that i do not know i solve.
The xAxis have the number of index of the key parameter and not the key parameters values;
When I sort the data after pressing the "sort" button;
The numbers over the bars after the sort disappear or appear the the left side insted of over the correspondent bars
var dataset = {key: [0, 30], value:[60, 30]};
http://jsfiddle.net/3HL4a/75/
Here is the link where you can see the code and perform the changes.
Thanks
To reference the key values, you have to use those instead of the index:
var xScale = d3.scale.ordinal()
.domain(dataset.key)
.rangeRoundBands([0, w], 0.05);
// ...
.attr("x", function(d, i) {
return xScale(dataset.key[i]);
})
You can make this nicer by merging the key and value arrays such that both are part of the same data element.
As for the other problem, this is because you're selecting text elements, which include the axis labels. Even with the key function this doesn't work here, as one of the axis labels is the same as the label you want to display on the bar. Assigning a special class to these labels and selecting accordingly fixes this.
See here for the complete jsfiddle.
I'm trying to create a bar chart with custom values for each bar along the xAxis in D3, and thought I'd try to use the tickValues. Just to try it out I gave it some dummy data but it doesn't work like this:
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickValues(['hi','b','c','d','e']);
Here's the xScale:
gon.votes.length is an array of total votes counts for EACH answer and functionally is there only to return how many bars there will be for a specific survey question(this is a survey app fyi)
var xScale = d3.scale.linear()
.domain([0,gon.votes.length])
.range([0,w]);
Finally, when I call
function(d){ return d.title}
This will extract the appropriate title for each bar.
Also here is the html of the ticks of the xAxis that gets rendered:
NaN
Any tips on how to put this along the xAxis? Is trying to modify the ticks the wrong approach?
Ah, I see you were using a linear scale. In this case you need an ordinal scale, since the domain is a set of discrete values (the list of titles).
Working example:
http://tributary.io/inlet/5775047
There are comments in the code for the axis part.
The issue is that the range array needs to have as many values as there are labels in the domain array. Otherwise it recycles. In your case, the first label was on 0, then w, then 0, then w, and so on.
To get the titles dynamically from the array of data, you need to use the map method on the array. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Working example with dynamic titles:
http://tributary.io/inlet/5775215
After using data().enter().append() in D3.js, individual columns/values in a dataset can be retrieved by simply using d.valuename. But I want to find the maximum value in a CSV column for a linear scale. Since the scale is not preceeded by any data calls, I'm really uncertain on how to specify the right column from which to find the maximum value.
This is my failed attempt. What do I substitute d.column1 with?
d3.csv("file.csv", function (data) {
var x = d3.scale.linear()
.domain([0, d3.max(d.column1)])
.range([0, 960]);
EDIT:
Ok, I got a bit further by looking at a similar example. I don't understand why my code is not working now.
d3.csv("file.csv", function (data) {
data.forEach(function(d) {
d.column1 = +d.column1;
});
var max = d3.max(data, function(d) { return d.column1; });
var x = d3.scale.linear()
.domain([0, max])
.range([0, 960]);
EDIT #2:
In case the problem derives from the data itself, here's a snippet. I have tried removing all quotes, and also keeping the quotes for everything but the top row, but nothing works.
"product", "price"
"bread",20
"meat",100
"meat, sausage",200
EDIT #3:
Ok, I promise, last edit. But just to be absolutely clear; I'm using the x-scale to determine the width of the bars in a bar chart: .attr("width", x) and the scale returns NaN.
To explain why your code is working:
data.forEach(function(d) {
d.column1 = +d.column1;
});
This above statement turns all your column1 values into an integer (the + sign in front of d.column1 coerces the type to a Number).
Once your values are all numbers, d3 can now compare them as numbers instead of strings. This way 10 will be larger than 9. If they were strings, "9" would be larger than "10".
var max = d3.max(data, function(d) { return d.column1; });
The above line simply takes the max of your data array. Since your data array is made up of objects, d3 has no way of knowing how to compare one object to another. The second parameter is a function that gives d3 a value to each object so that d3 can compare each object. In your case you are telling d3 to use column1 to determine the max value. This is similar to a comparator function.
I just realized what the problem was. No wonder we couldn't figure this one out together. All the code above is fine. It's just that when I called the x-scale to determine the width of my bars, I didn't specify which column in the data would be input to the scale. I had
.attr("width", x)
But now I changed that row to:
.attr("width", function(d) { return x(d.column1); })
Thanks for your help and patience.