I've set the scale of my bar chart with a time domain, from 1985 to 2015. D3 displays it nicely; however, there is an overlapping on some bars. I could reduce their width, but it would be too small. See screenshot here : http://imgur.com/y8QDbqn.
Is there any way to force the bars to align one after the other, even if it requires to cheat a little bit on the alignement?
Some snippets of code:
var x = d3.time.scale()
.range([0, width]);
x.domain(d3.extent(data, function(d) { return d.date; }));
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", function(d) { return color(d.parti); })
.attr("x", function(d) { return x(d.date); })
.attr("width", 5)
.attr("y", function(d) { return y(d.nombre); })
.attr("height", function(d) { return height - y(d.nombre); });
(Full code here : https://gist.github.com/992502bf6118da4b7b6b.git)
Thank you!
Related
Looking to stylize a bar chart using a D3 SVG. Right now it contains a number on the y axis and, on the x axis, day by day labels. There is a tick mark for each day, where I want only the first date of the week displayed. How can I show just one a week? Code below:
d3.csv("us-counties-cases.csv", function(data) {
filteredData = data.filter(function(row) {
return row['county'] == 'New York City';
});
var x = d3.scaleBand()
.range([ 0, width])
.domain(filteredData.map(function(d) {
return d.date;
}))
.padding(0.2);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll("text")
.attr("transform", "translate(-10,0)rotate(-45)")
.style("text-anchor", "end");
// Add Y axis
var max = d3.max(filteredData, function(d) { return d.cases; });
var y = d3.scaleLinear()
.domain([0, max * 1.2])
.range([ height, 0]);
svg.append("g")
.call(d3.axisLeft(y));
// Bars
svg.selectAll("mybar")
.data(filteredData)
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.date);
})
.attr("width", x.bandwidth())
.attr("fill", "#b3b3b3")
.attr("height", function(d) {
return height - y(0);
})
.attr("y", function(d) {
return y(0);
})
// Animation
svg.selectAll("rect")
.transition()
.duration(200)
.attr("y", function(d) {
return y(d.cases);
})
.attr("height", function(d) {
return height - y(d.cases);
})
.delay(function(d,i){
return(i*50)
})
})
Usually you can use axis.tick() to set the arguments that controls how ticks are displayed. Among other things, you can set how many ticks to visualize.
However, as you can read in the documentation, the effect of this method and its arguments depends on the axis’ scale type. If you are using scaleBand, axis.tick() has no effect and you should use axis.tickValues to set the tick values explicitly. Similarly, if you want to change the tick format, it's necessary to use axis.tickFormat.
For your specific case, the following code modifies the x axis to show only one tick every 7 days:
d3.axisBottom(x).tickValues(x.domain().filter( (d,i) => !(i % 7) ))
I am developing a two-way bar chart using d3. I want to add grid lines to the bar chart, how to customize those grid lines. I've used d3fc to draw the grid lines. It looks something like
var x = d3.scaleLinear()
.rangeRound([0, width])
var y = d3.scaleBand()
.rangeRound([0, height]).padding(0.5);
var xAxis = d3.axisBottom(x)
.ticks(8)
.tickSize(0)
.tickFormat(function(d){
return d3.format('.00s')(Math.abs(d)); // Use Math.abs() to get the absolute value
});
var yAxis = d3.axisLeft(y)
.ticks(5)
.tickSize(0);
//draw grid lines
const gridline = fc.annotationSvgGridline()
.xScale(x)
.yScale(y);
var svg = d3.select("#ageSexNotif").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain([-d3.max(data, function(d){
return d.female
})*1.2,d3.max(data, function(d){
return d.female
})*1.2])
y.domain(data.map(function (d) {
return d.age;
}));
svg.append("g")
.attr("class", "x axis")
.call(xAxis)
.call(gridline);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// .call(gridline);
var barsRight = svg.selectAll(".bar")
.data(data)
.enter().append("g")
barsRight.append("rect")
.attr("class", "bar")
.attr("x", function (d) {
return x(Math.min(0, d.female));
})
.attr("y", function (d) {
return y(d.age);
})
.attr("width", function (d) {
return Math.abs(x(d.female) - x(0));
})
.transition()
.duration(1500)
.delay(200)
.style("margin-top", "10px")
.attr("height", y.bandwidth())
.attr("fill", "#F293C9")
.attr("text", "label");
barsRight.append("text")
.attr("class", "label")
//y position of the label is halfway down the bar
.attr("y", function (d) {
return y(d.age) + y.bandwidth()- 6;
})
//x position is 3 pixels to the right of the bar
.attr("x", function (d) {
return x(d.female) + 10;
})
.text(function (d) {
return (d.female/1000)+'k';
})
.style("font-family", "Source Sans Pro")
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill", "#F293C9");
var barsLeft = svg.selectAll(".bar2")
.data(data)
.enter().append("g")
barsLeft.append("rect")
.attr("class", "bar2")
.attr("x", function (d) {
return x(Math.min(0, -d.male));
})
.attr("y", function (d) {
return y(d.age);
})
.attr("width", function (d) {
return Math.abs(x(-d.male) - x(0));
})
.transition()
.duration(1500)
.delay(200)
.style("margin-top", "10px")
.attr("height", y.bandwidth())
.attr("fill", "#4880FF");
barsLeft.append("text")
.attr("class", "label")
.style("font-family", "Source Sans Pro")
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill","#4880FF")
//y position of the label is halfway down the bar
.attr("y", function (d) {
return y(d.age) + y.bandwidth()- 6;
})
//x position is 3 pixels to the right of the bar
.attr("x", function (d) {
return x(-d.male) - 40;
})
.text(function (d) {
return (d.male/1000)+'k';
});
The result of my chart is
My chart should look like this
How to join the edges in x-axis and highlight the base axis as shown in the image? Any help for customizing the grid lines is appreciated.
Link to my example link
Thanks in advance!
You can add class name to your grid lines using attr('class', 'class-name'), and add your effect by CSS.
I've made some changes to your pen that you can see here.
The main changes:
join the edges in x-axis
If you remove the *1.2 multiplier from the domains and add .nice() then the x-axis will be squared off with the gridlines.
var x = d3.scaleLinear()
.rangeRound([0, width])
.domain([-d3.max(data, d => d.female), d3.max(data, d => d.female)])
.nice();
highlight the base axis
We can do this using an annotation line from d3fc.
const line = fc.annotationSvgLine()
.xScale(x)
.yScale(y)
.orient('vertical')
.label('')
.decorate(selection => {
selection.select('line')
.attr('stroke', 'black');
});
svg.datum([0]).call(line);
Here we are creating an annotation line using our scales. We set the line to be vertical and we remove the default label by replacing it with an empty string. After that we use the decorate function to colour the line black.
customizing the gridlines
We can control the opacity of the gridlines using a decorate function.
const opacityDecorate = selection => {
selection.attr('opacity', 0.2);
};
const gridline = fc.annotationSvgGridline()
.xScale(x)
.yScale(y)
.xDecorate(opacityDecorate)
.yDecorate(opacityDecorate);
Here we are using a decorate function to set the opacity for the both the horizontal and vertical gridlines. Alternatively you could also use different decorate functions to style the horizontal and vertical lines differently.
I hope this helps!
var y = d3.scale.ordinal()
.rangeRoundBands([height,0], .1)
var x = d3.scale.linear()
.rangeRound([0, width],.1);
{......}
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", y.rangeBand())
.attr("height", function(d) { return x(d.x1)-x(d.x0); })
.style("fill", function(d) { return color(d.name); });
I don't know why my bar rotated 180 degree, I want the y-axes show the bar. So it will look like vertical bar.
Please help! Thank you!
I guess the title explains the problem. Similar questions on SO refer to ordinal scales.
x axis scale:
var x = d3.time.scale()
.domain(d3.extent(data, function(d) {
return d.date;
}))
.range([width/data.length/2, width-(width/data.length/2)]);
The bars
bars.enter()
.append("rect")
.classed("column", true)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.attr("width", (width-(width/data.length)) / (data.length+2))
.attr("x", function(d) { return x(d.date) - (width/data.length)/2; })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value);});
How can I amend this code so that I have a whole and equal number of pixels between each vertical bar, and thereby getting an even spacing ?
I'm drawing a chart but have trouble how to handle my y scale and that my rectangles are drawn "upon" each over. (I draw with the bars from y-axis)
I calculate how high each bar needs to be and if a bar is higher than my default height the y does not manage to handle this. I'm not shure how to handle this. I draw my rect:
forms.append("rect")
.attr("class", "rectStyle")
.style("fill", function(d) { return color(d.Name); })
.attr("x", function(d){ return x(new Date(d.startDate));})
.attr("height",function(d) { return d.barHeight;})
.attr("y", function(d) { return y(d.RowGroup);})
.attr("width", function(d){ d.w = (x(new Date(d.endDate))-x(new Date(d.startDate)));
return d.w; });
y = d3.scale.linear()
.domain([1,noRows])
.range([0,height -margin.top-margin.bottom-padding*noRows]);
Grateful for how I should solve this problem