I would like to create a split difference bar chart using d3 like the attached image. I have 2 input arrays one for y-axis labels and one for data as below:
data = [[35,90], [60,45], [80,90], [95,90]]
months = ["Jan - 20", "Feb - 20", "Mar - 20", "Apr -20"]
I tried my hands in d3-observable. Feel free to fork this observable.I am stuck at getting the nested data rendered properly with scale. Any help would be much appreciated.
Thanks
You can indeed avoid SVG and use vanilla HTML to draw a chart like that. Of course, this doesn't mean you can't use d3 to make your life easier - it has methods to simplify the handling of scales, date formatting and the creation of elements which isn't in any way restricted to SVG. For example, you can create the first bar chart and its label like so:
d3.select("#first-bar-column")
.selectAll("div")
.data(data)
.enter()
.append("div")
.style("width", d => `${xScale(+d.A)}px`)
.classed("first-bar", true)
.append("p")
.classed("label", true)
.text(d => d.A);
Have a look at this fiddle for the full example. If you want any "special effects" to apply to the entire row on hover / click, you'd need to assign some common class based on the element index: .attr("class", (d, i) => `row${i}`)
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 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'm new to StackOverflow and I just started using D3.
I need to show the values on a map. I saw this question that is very similar to what I should do.
What I'd like is to color the countries based on the values in the column Date to a CSV and based on selected year by user (radio button).
How can I do that?
I created a gray color scale and have included them in an array, then I created a method chooseColor(value) that returns the correct color based on the value of the country in that year.
I think it is not the most efficient method to do this thing...
Also in my CSV there are not all the countries present in the European Union. For example, I have no data on Russia so I "turned off" some countries putting an if inside the event on mouseover.
But I would also cut part of Russia in the map in order to enlarge the known countries. How can I do also that thing?
I looked at these examples: Choropleth and Threshold Choropleth by Mike Bostock on bl.ocks.org but I have not understand how to color the countries...
(I wanted to put links but I can't post more than 2 links because of my low reputation)
This is my code.
I apologize for my bad English. Thank you all,
Pier
EDIT
I admit I did not understand some things in your code.
Why I need events on mouseover and mouseout? And what are hover and rhover? I thought they were events related to this question. But in my case I don't need it, no?
Use array_values or d is the same, right? Does not change if I use d or array_values, right? It is a stupid question but it confused me.
I modified the makemap method in this way. I understand correctly how to use your code?
function makemap(error, europe, dessease) {
dess = dessease.slice();
counties = topojson.feature(europe, europe.objects.collection);
vector = svg.selectAll("path")
.data(counties.features)
.enter()
.append("path")
.attr("class", "county")
.attr("id", function(d) {
return "coun" + d.properties.indx;
})
.attr("d", path)
.style("fill", function(array_values) {
return color(array_values[d.country]);
});
In this case there is an error concerning d, of course. Sorry, I do not know where I'm wrong...
The country's color will depend on a value. So the color IS a function of "value". To do that you must to define a range of color based on your values:
var color = d3.scale.linear()
.domain([mn,mx]) // <--- min and MAX of your value
.range(["#ffffff","000000"]);
then define the color of your country:
svg.selectAll(".county")
.style("fill", function(array_values) {
return color(array_values[d.country]);
});
Must-Read: Jerome Cukier - d3: scales, and color
I have a series of horizon graphs that have been created like this:
d3.select("body")
.selectAll(".horizon")
.data(metrics)
.enter()
.append("div")
.attr("class", "horizon")
.attr("id", function(d){return d.toString();})
.call(context.horizon().height(['75']));
where metrics is an array of metric objects.
I want to redefine the extent for one of those horizon objects.
I've tried calling the extent function on the one graph, but I'm not sure if I'm calling it, or even selecting it, correctly:
d3.select("#id-attribute")
.call(context.horizon().extent(function(d,i){return([0,1000]);}));
This sort-of seems to work, but the graph display gets screwed up, with additional whitespace being added below the graph and the motion of the graph not accounting for that and leaving the top of the graph unanimated. I suspect that it's in some way due to it being a new instance of the extent object, so I tried this:
d3.select("#id-attribute")
.call(extent(function(d,i){return([0,1000]);}));
but that generates: "ReferenceError: extent is not defined".
I've tried redefining the metric's extent function, effectively:
metrics[3].extent = function(d,i) {return([0,100]);};
but that causes the graph to be blank (although mousing over it reveals numbers in the readout), and causes its readout and the readouts of the graphs that appear below it to be blank when the mouse is not hovering over any of the graphs.
I honestly have no comprehension of how this stuff fits together, nor am I particularly experienced with JavaScript, so I'm not sure where my error lies. I'm totally out of my depth. Any tips would be greatly appreciated.
Do you need to redefine the extent after the initial call where the horizons are generated?
If not, try something like this:
d3.select("body")
.selectAll(".horizon")
.data(metrics)
.enter()
.append("div")
.attr("class", "horizon")
.attr("id", function(d){return d.toString();})
.call(context.horizon()
.height(['75'])
.extent(function(d,i){ //use this function to change extents
if (d.toString() == 'Whatever the name is'){
return [0,1000];
} else {
return [ <default min>, <default max>]
}
})
)
You can either set your own default extent, or use a function like d3.min()/d3.max() to get the value for the horizons. You just need to return an array with two elements.
I am fairly new to D3 and I am trying create a modified version of the icicle chart here. I need to add labels above the chart to specify the hierarchy level name
( not the name of each partition but what column name represents the level in the hierarchy).
I have the names in an array and I have tried to add them to the chart before I bind the actual hierarchical data but I cannot seem to get the labels appear above that chart.
I am not sure whether I need to reduce the amount of vertical space the chart takes up or I need to move the labels for each hierarchy level
var levels=["LEV 1", "LEV 2", "LEV 3", "LEV 4"];
vis.selectAll("g").append('g')
.data(levels).enter()
.append("text")
.attr("dy", ".55em")
.attr('y', 5)
.attr('x', function(d,i){
return (i+1)* (w / levels.length) ;})
.attr('text-anchor', 'start')
.attr("class", "pHeader")
.text(function(d) { return d; });
any help would be greatly appreciated
It seems to me your problem is just a matter of leaving room for a margin when you set the size of your plotting area.
This tutorial should help:
D3 Margin Convention
You might also want to look at using a D3 axis with an ordinal scale for spacing out your level labels.