d3.js scaleBand()'s ticks coming out of place - javascript

I am using scaleBand() for both x and y axes for a bar chart. For some reason, the height of the bars are in between the ticks of the y axis. I would appreciate any help. Here is my code:
var margin_ = { top: 20, right: 20, bottom: 30, left: 40 },
width_ = 960 - margin_.left - margin_.right,
height_ = 500 - margin_.top - margin_.bottom;
var svg_ = d3.select("body").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 + ")");
var x = d3.scaleBand()
.range([0, width_])
.padding(0.2)
var y = d3.scaleBand()
.range([height_, 0]);
x.domain(satisfactScaleKeyValues);
y.domain(graphYvalues);
svg_.selectAll(".bar")
.data(datas)
.enter().append("rect")
.attr("class", "bar__")
.attr("x", function (d) { return x(d.variable); })
.attr("width", x.bandwidth())
.attr("y", function (d) { return y(d.satisLevel); })
.attr("height", function (d) { return height_ - y(d.satisLevel); });
// add the x Axis
svg_.append("g")
.attr("transform", "translate(0," + height_ + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg_.append("g")
.call(d3.axisLeft(y))

You should use d3.scalePoint, which would provide a better translation from an ordinal domain to linear points on a range:
let datas = [{variable: 1, satisLevel: "Neutral"}]
var margin_ = { top: 20, right: 50, bottom: 30, left: 75 },
width_ = 960 - margin_.left - margin_.right,
height_ = 500 - margin_.top - margin_.bottom;
var svg_ = d3.select("body").append("svg")
.attr("width", width_ + margin_.left + margin_.right)
.attr("height", height_ + margin_.top + margin_.bottom)
var g = svg_.append("g")
.attr("transform",
"translate(" + margin_.left + "," + margin_.top + ")");
var x = d3.scaleBand()
.range([0, width_])
.padding(0.2)
var y = d3.scalePoint()
.range([height_, 0])
.padding(0.2)
x.domain([1]);
y.domain(["Not satisfied", "Neutral", "Satisfied"]);
g.selectAll(".bar")
.data(datas)
.enter().append("rect")
.attr("class", "bar__")
.attr("x", function (d) { return x(d.variable); })
.attr("width", x.bandwidth())
.attr("y", function (d) { return y(d.satisLevel); })
.attr("height", function (d) { return height_ - y(d.satisLevel); });
// add the x Axis
g.append("g")
.attr("transform", "translate(0," + height_ + ")")
.call(d3.axisBottom(x));
// add the y Axis
g.append("g")
.call(d3.axisLeft(y))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
If you want the height of the lowest Satisfaction Level to be more than zero, you can add padding to the scalePoint, like in the example.

Related

Unable to get stacks in d3 bar chart

I would like to know what i am doing wrong i think its in the axis as the stacked data as well as well as the rect elements are showing. Been stuck now for 2 hours.
var margin = {top: 10, right: 30, bottom: 20, left: 50},
width = 760 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
innerheight=height + margin.top + margin.bottom
d3.csv("data.csv", function (error, data) {
if (error) {
throw error;
}
// var subgroups = data.columns.slice(1);
// console.log(subgroups);
var subgroups = d3.map(data, function(d){ return(d.Segment)}).keys();
var groups = d3.map(data, function(d){ return(d.Category)}).keys();
console.log(groups);
// Add X axis
var x = d3.scaleBand()
.domain(groups)
.range([0, width])
.padding([0.2])
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSizeOuter(0));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.Sales; })])
.range([ height , 0 ]);
console.log(y.domain())
console.log(y.range())
svg.append("g")
.call(d3.axisLeft(y));
var color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a']);
ymax= d3.max(data, function(d) { return d.Sales; });
console.log(subgroups);
var stackedData = d3.stack().keys(subgroups);
stackedData = stackedData(data)
console.log(stackedData);
svg.append("g")
.selectAll("g")
// Enter in the stack data = loop key per key = group per group
.data(stackedData)
.enter().append("g")
.attr("fill", function (d) { return (color(d.key)); })
.selectAll("rect")
// enter a second time = loop subgroup per subgroup to add all rectangles
.data(function (d) { return d; })
.enter().append("rect")
.attr("x", function (d) { return x(d.data.Category); })
.attr("y", function (d) { return y(d.data.Sales); })
.attr("height", function (d) { return (height - y(d.data.Sales)) ; })
.attr("width", x.bandwidth())
});
csv is in this format
Row ID,Segment,Category,Sales,Total
1,Consumer,Accessories,84754.742,1007859.426
2,Consumer,Appliances,42852.68,1007859.426
31,Corporate,Phones,89019.204,588163.8014
34,Corporate,Tables,57226.0385,588163.8014
37,Home Office,Art,4799.502,373365.8573

d3 timeline update issue

I got this piece of code in which the timeline updates his value by sliding(manually). Problem is That I got an error saying that projection is not a code. Can someone help me please.
Setting the size of the slider.
var margin = {
top: 50,
right: 90,
bottom: 50,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 300 - margin.bottom - margin.top;
// scale function
var timeScale = d3.time.scale()
.domain([new Date('2016-01-01'), new Date('2016-12-31')])
.range([0, width])
.clamp(true);
// initial value
var startValue = timeScale(new Date('2012-03-20'));
startingValue = new Date('2016-08-12');
//////////
defines brush
var brush = d3.svg.brush()
.x(timeScale)
.extent([startingValue, startingValue])
.on("brush", brushed);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
// classic transform to position g
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
// put in middle of screen
.attr("transform", "translate(0," + height / 2 + ")")
// inroduce axis
.call(d3.svg.axis()
.scale(timeScale)
.orient("bottom")
.tickFormat(function(d) {
return formatDate(d);
})
.tickSize(0)
.tickPadding(12)
.tickValues([timeScale.domain()[0], timeScale.domain()[1]]))
.select(".domain")
.select(function() {
console.log(this);
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "halo");
var slider = svg.append("g")
.attr("class", "slider")
.call(brush);
slider.selectAll(".extent,.resize")
.remove();
slider.select(".background")
.attr("height", height);
var handle = slider.append("g")
.attr("class", "handle")
handle.append("path")
.attr("transform", "translate(0," + height / 2 + ")")
.attr("d", "M 0 -20 V 20")
handle.append('text')
.text(startingValue)
.attr("transform", "translate(" + (-18) + " ," + (height / 2 - 25) + ")");
slider
.call(brush.event)
Sets the canvas with the projection.
var width = 1280;
var height = 900;
var projection = d3.geo.mercator()
.scale(200000)
.center([4.9, 52.36])
.translate([width / 2, height / 2]);
var canvas = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", function () {
canvas.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")")
}))
.append("g")
Logs the events of the day and the coordinates to the console.
function brushed() {
var value = brush.extent()[0];
console.log()
if (d3.event.sourceEvent) { // not a programmatic event
value = timeScale.invert(d3.mouse(this)[0]);
brush.extent([value, value]);
}
handle.attr("transform", "translate(" + timeScale(value) + ",0)");
handle.select('text').text(formatDate(value));
slidedate = (formatDate(value));
console.log(slidedate)
console.log("dit is datum" + slidedate)
dataCsv.forEach(function(d) {
//console.log("datum is" + slidedate)
if (slidedate == d.Datepattern_startdate) {
console.log(slidedate)
console.log(d.Title)
coords = projection([+d["Longitude"],+d["Latitude"]]);
console.log(coords);
var circle = canvas.append("circle")
.attr("cx", coords[0] )
.attr("cy", coords[1] )
.attr("r", "6px")
.attr("fill", "orange")
.style("opacity", .6)
.append("title")
.text(function(dataCsv) {return d.Title})
} else {
//console.log(" Werkt niet")
}
});
Error = mapm.html:272 Uncaught TypeError: projection is not a function.

Drawing stacked-bar chart using d3

I'm trying to adapt this code:
http://bl.ocks.org/anupsavvy/9513382
To plot a stacked-bar chart using custom data. I don't need any transitions, just a simple plot.
I end up with this code:
data = jsonArr;
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var color = d3.scale.ordinal()
.range(colorrange);
var stack = d3.layout.stack();
stack(data);
var xScale = d3.time.scale()
.domain([new Date(0, 0, 0, data[0][0].label, 0, 0, 0), new Date(0, 0, 0, data[0][data[0].length - 1].label, 0, 0, 0)])
.rangeRound([0, width - margin.left - margin.right]);
var yScale = d3.scale.linear()
.domain([0,
d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.value;
});
})
])
.range([height - margin.bottom - margin.top, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(d3.time.hour, 1)
.tickFormat(d3.time.format("%H"));
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10);
var svg = d3.select("#info")
.append("svg")
.attr("width", width)
.attr("height", height);
var groups = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("class", "rgroups")
.attr("transform", "translate(" + margin.left + "," + (height - margin.bottom) + ")")
.style("fill", function(d, i) {
return colorrange[i];
});
var rects = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("width", 6)
.attr("height", function(d) {
return -yScale(d.value) + (height - margin.top - margin.bottom);
})
.attr("x", function(d) {
return xScale(new Date(0, 0, 0, d.label, 0, 0, 0));;
})
.attr("y", function(d) {
return -(-yScale(d.y0) - yScale(d.value) + (height - margin.top - margin.bottom) * 2);
});
console.log(rects);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(40," + (height - margin.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(yAxis);
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - 5)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.text("Number of complaints");
svg.append("text")
.attr("class", "xtext")
.attr("x", width / 2 - margin.left)
.attr("y", height - 5)
.attr("text-anchor", "middle")
.text("Hour of day");
More specifically:
yScale(d.y0) is returning NaN.
If I comment this piece of code, I can see the axes:
After a while, I managed to see the and some data (among errors):
I guess I'm not understanding the properly way to plot the data itself.
Any help would be appreciated.
My json label attribute is related to the y coordinate, while value is related to the x coordinate.
EDIT:
It seems that the problem begins when I call stack. The first array has y0 values as 0, but the second and third ones have y0 = NaN. I don't know how to fix this.
This is the relative jsfiddle:
https://jsfiddle.net/rhzkz9gb/13/
You need to provide the accessor functions for the data (because it is not keyed with 'x' and 'y').
var stack = d3.layout.stack().x(function(d,i){return i;}).y(function(d){return d.value;});
https://jsfiddle.net/ermineia/rhzkz9gb/14/

Y and Height Values for Stacked Bar Chart in D3

I'm trying to render a stacked bar chart from a multidimensional array that is grouped together by stack, and I can't figure out how to configure the y and height values of each rectangle. The tutorial I've been going off is here, but their data structure varies greatly from mine. I believe the solution is in how the y0 and y1 values are determined, but being a novice in D3 I'm unable to discern how it's being computed in the tutorial.
Here's what I have so far (the returned data from the data and legendLabels variables is at the top:
data = [["Privés dans les trois dimensions","30.2"],["Privés dans une dimension additionelle","24.4"],["Privés seulement dans la dimension spécifiée","8.32"],["Privés dans les trois dimensions","30.2"],["Privés dans une dimension additionelle","26.1"],["Privés seulement dans la dimension spécifiée","3.75"],["Privés dans les trois dimensions","30.2"],["Privés dans une dimension additionelle","33.1"],["Privés seulement dans la dimension spécifiée","10.4"]]
legendLabels = ["Nutrition","Santé","Eau"];
var $chart = $(chart),
data = $chart.data("chartDataTest"),
legendLabels = $chart.data("chartLabels"),
groupedData = [];
$.each(legendLabels, function(i){
var labelLength = legendLabels.length,
dataLength = data.length;
var sliceBeginning = i * labelLength,
sliceEnd = sliceBeginning + labelLength;
dataRange = data.slice(sliceBeginning, sliceEnd);
groupedData.push(dataRange);
});
var yValues = [];
$.each(groupedData, function(i, dataGroup){
var subGroupY = 0;
$.each(dataGroup, function(ii, dataSubGroup){
subGroupY += +dataSubGroup[1];
});
yValues.push(subGroupY);
});
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select(chart).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "parent-group")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(legendLabels.map(function(d) { return d; }));
x0.domain(groupedData.map(function(d) { return d; }));
y.domain([0, d3.max(yValues, function(d) { return d; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var group = svg
.selectAll(".group")
.data(groupedData)
.enter()
.append("g")
.attr("class", "g")
.attr("transform", function(d) {
return "translate(" + x0(d) + ",0)";
});
var yPosition = 0;
group
.selectAll(".group")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d, i) {
return y(d[1]);
})
.attr("height", function(d) {
return y(d[1]);
});
Here's what my chart looks like so far:
Per #CoolBlue's suggestion, I rerefactored this to use d3's stack function. I based my new approach on this tutorial. Here's the updated code:
var $chart = $(chart),
data = $chart.data("chartDataTest"),
chartStacks = $chart.data("chartLabels"),
groupedData = [],
colors = [];
var width = 900,
height = 500,
margin = { top: 10, right: 50, bottom: 30, left: 50},
x = d3.scale.ordinal().rangeRoundBands([0, width - margin.left - margin.right]),
x0 = d3.scale.ordinal().rangeRoundBands([0, width - margin.left - margin.right], 0.1),
y = d3.scale.linear().range([0, height - margin.top - margin.bottom]);
y0 = d3.scale.linear().range([height - margin.top - margin.bottom, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom"),
yAxis = d3.svg.axis()
.scale(y0)
.orient("left")
.tickFormat(d3.format(".2s"));
$.each(chartStacks, function(i){
var labelLength = chartStacks.length,
dataLength = data.length;
var sliceBeginning = i,
dataRange = data.slice(sliceBeginning);
colorGroup = [];
$.each(dataRange, function(ii, colorData){
if ( ii % labelLength == 0) {
colorGroup.push(colorData[1]);
}
});
var remappedValues = colorGroup.map(function(dat, i) {
return {
x: i,
y: +dat
}
});
var remappedColors = dataRange.map(function(dat, i) {
return dat[0];
});
colors.push(remappedColors[0]);
groupedData.push(remappedValues);
});
var stacked = d3.layout.stack()(groupedData);
x0.domain(chartStacks.map(function(d) { return d; }));
x.domain(stacked[0].map(function(d) { return d.x; }));
y.domain([0, d3.max(stacked[stacked.length - 1], function(d) { return d.y0 + d.y; })]);
y0.domain([0, d3.max(data, function(d) { return 90; })]);
var svg = d3.select(chart).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ", " + 46 + ")")
.call(yAxis);
var svgContent = svg
.append("g")
.attr("class", "parent-group")
.attr("transform", "translate(" + margin.left + "," + height + ")");
var valGroup = svgContent
.selectAll(".parent-group")
.data(stacked)
.enter()
.append("g")
.attr("class", "valgroup");
var rect = valGroup.selectAll("rect")
.data(function(d){return d;})
.enter().append("svg:rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand() - 15);

Y-axis transition results in changing side

I'm having a bizarre d3 issue wherein my y-axis transition results in the axis switching sides. Besides that, I am happy with how the transition looks. A gif that demonstrates the issue is available at https://i.imgflip.com/g4kdc.gif. I have followed a few examples including http://bl.ocks.org/mbostock/4323929, but can't seem to figure out the issue.
var dataset = [{
"year_decided": 1982,
"Total": 0
}, //some more data
{
"year_decided": "2000",
"Total": "310"
}]; // default dataset that we can access and override
var margin = {
top: 30,
right: 20,
bottom: 20,
left: 35
};
var w = $("#section").width() - margin.left - margin.right;
var h = $("#section").height() - margin.top - margin.bottom;
///create the default scales
var xScale = d3.scale.linear()
.domain([0,dataset.length])
.range([0,w]);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset,function(d){ return +d.Total }) ])
.range([h,0]);
// create the axes
var xAxis = d3.svg.axis()
.scale(xScale)
.tickSize(6, 0)
.orient("bottom")
.tickFormat(function(d,i){
return d + 1982;
});
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("right")
.tickSize(w);
//define the svg and add it to the document
var svg = d3.select("#section").append("svg")
.attr("width",w + margin.left + margin.right)
.attr("height",h + margin.top + margin.bottom)
.attr("id", "main-chart");
svg.append("g")
.attr("transform","translate(" + margin.left + "," + margin.top + ")");
//add an axis group and add the y axis
var gy = svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.call(customAxis)
var gx = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + h + ")")
.call(xAxis);
//create the line
var line = d3.svg.line()
.x(function(d, i){ return xScale(i) })
.y(function(d){ return yScale(+d.Total) })
.interpolate("linear");
svg.append('path')
.classed('data-line', true)
.attr('d', line(dataset))
.attr("transform", "translate(" + margin.left + ",0)");
//create the tooltip
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5)
.attr("transform", "translate(" + margin.left + ",0)")
;
var tool_tip_year = focus.append("text")
.attr("x", 9)
.attr("dy", ".35em")
.attr("transform", "translate(" + margin.left + ",0)")
;
var tool_tip_total = focus.append("text")
.attr("x", 9)
.attr("dy", "1.8em")
.attr("transform", "translate(" + margin.left + ",0)")
;
svg.append("rect")
.attr("class", "overlay")
.attr("width", w)
.attr("height", h)
.attr("transform", "translate(" + margin.left + ",0)")
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
var bisectIndex = d3.bisector(function(d, i) { return i; }).left;
function mousemove() {
var x0 = xScale.invert(d3.mouse(this)[0]),
i = Math.round(x0),
d = dataset[i];
focus.attr("transform", "translate(" + xScale(i) + "," + yScale(+d.Total) + ")");
tool_tip_year.text(1982 + i);
tool_tip_total.text(d3.format(",")(+d.Total));
}
function customAxis(g){
g.selectAll("text")
.attr("x",4)
.attr("dy",-4);
}
//update the graph following http://bl.ocks.org/d3noob/7030f35b72de721622b8
$("#submission").submit(function(){
//define url to request
var submission = "php/data.php?word=" + $("#submission-text").val();
//request json and save as dataset
d3.json(submission, function(error, json) {
if (error) return console.warn(error);
dataset = json;
//rescale the graphs
xScale = d3.scale.linear()
.domain([0,dataset.length])
.range([0,w]);
yScale = d3.scale.linear()
.domain([0, d3.max(dataset,function(d){ return +d.Total }) ])
.range([h,0]);
gy.transition()
.duration(2500)
.call(yAxis);
gy.call(customAxis);
svg.select(".data-line")
.transition()
.duration(2000)
.attr("d",line(dataset));
})
});

Categories