How to add plots on a small multiple visualization using d3 - javascript

Current situation: I already have a small multiple visualization for my data. What it represents is the stress intensity over time for six different days. It plots the graphs correctly. Now I wanted to add dots on the existing graph if the person smoked at that time. I am reading a csv file which consists of date, time, stress level and whether the person smoked or not (so 1 if they did and -1 if they didn't). I am using d3 v4.
This is what I am currently getting but the red dots are obviously in the wrong spot because they are showing up places I don't even have data.
What I wanted was for the red dots to be on the graph and represent the times the user smoked.
Code:
<script>
var margin = {top: 8, right: 10, bottom: 2, left: 10},
width = 1160 - margin.left - margin.right,
height = 100 - margin.top - margin.bottom;
var parseDate = d3.timeParse("%H:%M:%S");
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var area = d3.area()
.x(function (d) {
return x(d.time);
})
.y0(height)
.y1(function (d) {
return y(d.stress);
});
var line = d3.line()
.x(function (d) {
return x(d.time);
})
.y(function (d) {
return y(d.stress);
});
d3.csv("6000smokedData3.csv", type, function (error, data) {
// Nest data by date.
var dates = d3.nest()
.key(function (d) {
return d.date;
})
.entries(data);
// Compute the maximum stress per date, needed for the y-domain.
dates.forEach(function (s) {
s.maxPrice = d3.max(s.values, function (d) {
return d.stress;
});
});
// Compute the minimum and maximum time across dates.
// We assume values are sorted by time.
x.domain([
d3.min(dates, function (s) {
return s.values[0].time;
}),
d3.max(dates, function (s) {
return s.values[s.values.length - 1].time;
})
]);
// Add an SVG element for each date, with the desired dimensions and margin.
var svg = d3.select("body").selectAll("svg")
.data(dates)
.enter().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 + ")");
//Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 4)
.style("fill", function (d) {
return "red";
})
.attr("cx", function (d) {
if (d.smoked == 1) {
return x(d.time);
}
})
.attr("cy", function (d) {
if (d.smoked == 1) {
return y(d.stress);
}
});
// 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));
// Add the area path elements. Note: the y-domain is set per element.
svg.append("path")
.attr("class", "area")
.attr("d", function (d) {
y.domain([0, d.maxPrice]);
return area(d.values);
});
// Add the line path elements. Note: the y-domain is set per element.
svg.append("path")
.attr("class", "line")
.attr("d", function (d) {
y.domain([0, d.maxPrice]);
return line(d.values);
});
// Add a small label for the date name.
svg.append("text")
.attr("x", width - 6)
.attr("y", height - 6)
.style("text-anchor", "end")
.text(function (d) {
return d.key;
});
});
function type(d) {
d.stress = +d.stress;
d.time = parseDate(d.time);
d.smoked = +d.smoked;
return d;
}
</script>
Few lines of csv file:
date,time,stress,smoked
2014-08-04,11:24:28,0.026191,-1
2014-08-04,11:24:29,0.026183,-1
2014-08-04,11:24:30,0.031845,-1
2014-08-04,11:24:31,0.01235,-1
Thank you

You're drawing the dots before you set the y scale for each element. I usually like to make small multiples inside of an each loop to avoid tricky things like. It looks like the y axis is also off - they should be different on each plot.

Related

d3.v5.min.js:2 Error: <path> attribute d: Expected moveto path command ('M' or 'm'), "function a(a){va…"

I am trying to plot a multi-line graph with d3.js but I am getting this error:
Error: attribute d: Expected moveto path command ('M' or 'm'), "function t(t){va…".
I have been stuck at it a while now and tried everything I could think of, to help you in your reflections, Find the code that I use below.
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 60},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
const dateParser = d3.timeFormat("%Y-%m-%d %H:%M:%S");
data.forEach(function(d) {
d.ts = dateParser(new Date(d.ts));
d.value = parseFloat(d.value)
});
// append the svg object to the body of the page
var svg = d3.select("#graph")
.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 + ")");
//Read the data
// group the data: I want to draw one line per group
var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
.key(function(d) { return d.key;})
.entries(data);
// Add X axis --> it is a date format
var x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.ts; }))
.range([ 0, width ]);
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(5));
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.value; })])
.range([ height, 0 ]);
svg.append("g")
.call(d3.axisLeft(y));
// color palette
var res = sumstat.map(function(d){ return d.key }) // list of group names
var color = d3.scaleOrdinal()
.domain(res).range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf','#999999'])
// Draw the line
svg.selectAll(".line")
.data(sumstat)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 1.5)
.attr("d", function(d){
return d3.line()
.x(function(d) { return x(d.ts); })
.y(function(d) { return y(d.value); })
})
I cannot figure what I am doing wrong, in case the example I am looking at is this link. I am fairly new to d3 and it is not an easy library to use
When you set the d attribute, you return the line generator itself from the data linking function, but you fail to execute it. Configuring the generator and executing it is a two-step process:
first, contruct and configure the generator (line is a function)
var line = d3.line()
.x(function(d) { return x(d.ts); })
.y(function(d) { return y(d.value); })
then, pass the function to attr(), so it will be executed as line(d)
svg.selectAll(".line")
.data(sumstat)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 1.5)
.attr("d", line)

d3 bar graph error says xScale.bandwidth() is not a function

Can anyone tell me what I am doing wrong? I am getting an error.
Uncaught (in promise) TypeError: xScale.bandwidth is not a function
at barChart (bar_chart.js:53:27)
at bar_chart.js:84:5
I am trying to create a bar graph of this data.
year,total_ghg
2000,661.97
2001,665.72
2002,660.75
2003,583.65
2004,635.5
2005,598.44
2006,646.91
2007,646.46
2008,617.09
2009,633.8
2010,601.14
2011,644.74
2012,643.12
2013,555.26
2014,566.21
2015,566.47
2016,577.32
2017,623.08
2018,619.26
my js
var dataset;
function barChart(dataset) {
//declaring Varibales
var margin = {top:50, right:50, bottom:50, left:50};
var width = 500-margin.left-margin.right;
var height = 500-margin.top-margin.bottom;
//creating svg
var svg = d3
.select("#barchart")
.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 + ")");
//setting up scales
var xScale = d3
.scaleTime()
.domain([
d3.min(dataset, function (d) {
return d.year;
}),
d3.max(dataset, function (d) {
return d.year;
}),
])
.range([0,width]);
var yScale = d3.scaleLinear()
.domain([0,d3.max(dataset, function (d) {
return d.total_ghg;
})
])
.range([height,0]);
// Plotting axis
svg.append("g").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(xScale));
svg.append("g").call(d3.axisLeft(yScale));
//Set up groups
svg.selectAll("mybar")
.enter()
.data(dataset).append("rect")
.attr("x", function(d) {
return xScale(d.year);
})
.attr('y', function(d) {
return yScale(d.total_ghg);
})
.attr("width", xScale.rangeBand())
.attr('height', function(d) {
return height - yScale(d.total_ghg);
})
.attr("fill", "#004DA5")
.on("mouseover", function(event, d) {
d3.select(this).attr("fill", "orange");
var xPosition = parseFloat(d3.select(this).attr("x")) + xScale.bandwidth() / 2 - 5;
var yPosition = parseFloat(d3.select(this).attr("y")) + 20;
svg.append("text")
.attr("id", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition)
.text(d);
}).on("mouseout", function(d) {
d3.select("#tooltip").remove();
d3.select(this)
.attr("fill", "#004DA5")
});
}
function init() {
d3.csv("src/data/total_ghp.csv", function (d) {
// + : force the year and month to be typed as a number instead of string
return {
year: d3.timeParse("%Y")(d.year),
total_ghg: +d.total_ghg,
};
}).then(function (data) {
dataset = data;
barChart(dataset);
});
}
window.addEventListener("load", init);
Any suggestions Please
What I have tried
ScaleOrdinal
rangeBandRounds
RangeBand()
instead of bandwidth
and a few more things like using a different d3 script same error in every scenario
Your xScale uses scaleTime, which is meant for charts where the axis represents time (i.e. line charts). For bar charts you should use scaleBand instead which does have the bandwidth function:
const xScale = d3
.scaleBand()
.domain([
d3.min(dataset, (d) => d.year),
d3.max(dataset, (d) = > d.year),
])
.range([0, width]);
More information on scaleBand can be found here: https://observablehq.com/#d3/d3-scaleband
There are some other mistakes in your code that prevent your bars from rendering:
Replace scaleTime with scaleBand for xScale
Replace xScale.rangeBand() with xScale.bandwidth()
Move .enter() to come after .data(dataset)

How to integrate both AngularJS & d3.js

I'm very new to D3.js and created a chart and bound the data to it via an API call.The problem is when I'm trying to integrate this page with the rest of my angular application, it is making indefinite API calls for data fetch. I have included all the D3 reference in Index.html page. I have also attached an Image of how it is making so many calls to API. Please suggest a solution and apologies if I'm doing some silly mistake.
This is the code to call the D3.js page upon clicking a button.
$scope.resourceAnalytics = function (event) {
$window.open('/ResourceDetails', '_blank');
}
Routing to that page in app.js
$routeProvider.when("/ResourceDetails", {
templateUrl: '/Views/ResourceDetails.html',
});
and Please find below the a sample of how I'm making the API call from the chart
<script>
// set the dimensions and margins of the graph
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = 560 - margin.left - margin.right,
height = 260 - margin.top - margin.bottom;
// parse the date / time
var formatTime = d3.timeFormat("%d-%b");
var parseTime = d3.timeParse("%d-%b");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#linechart").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 + ")");
d3.json('http://localhost:51719/api/Admin/GetGenderDetails/', function (Jsondata) {
var data = Jsondata;
// format the data
data.forEach(function (d) {
d.GenderDate = formatTime(new Date(d.GenderDate));
//d.GenderDate = parseTime(d.GenderDate);
//d.Male = +d.Male;
//d.Female = +d.Female; // ConvertPercent
});
console.log(data)
// define the area
var area = d3.area()
.x(function (d) { return x(d.GenderDate); })
.y0(height)
.y1(function (d) { return y(d.Male); });
// define the area
var area2 = d3.area()
.x(function (d) { return x(d.GenderDate); })
.y0(height)
.y1(function (d) { return y(d.Female); });
// define the line
var valueline = d3.line()
.x(function (d) { return x(d.GenderDate); })
.y(function (d) { return y(d.Male); });
var valueline2 = d3.line()
.x(function (d) { return x(d.GenderDate); })
.y(function (d) { return y(d.Female); });
// scale the range of the data
x.domain(d3.extent(data, function (d) { return d.GenderDate; }));
//y.domain([0, d3.max(data, function(d) { return d.close; })]);
y.domain([0, d3.max(data, function (d) { return Math.max(d.Male, d.Female); })]);
// add the area
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area);
// add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
svg.append("path")
.data([data])
.attr("class", "area2")
.attr("d", area2);
// Add the valueline2 path.
svg.append("path")
.data([data])
.attr("class", "line")
.style("stroke", "#4DADDA")
.attr("d", valueline2);
// 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));
});
</script>
Multiple API calls made by D3

I am trying to visualize my json object with D3. I want date to be the x axis and y to be sales. number values stored a string

I have a json object that I am trying to visualize with D3.js. I want the x axis to represent the date in the json object which is stored as a string and the y axis to represent sales projections which is also a number in a string i.e "85,000.00"
example of my json object:
[{"Num":78689,"Client":"Health Services" ,"TotalEstSales":"85,000,000.00","Date ":"2/15/2015","RFP Receipt Date":null,"Exp. Proposal Due Date":"3/6/2015","Proposal Submission Date":null,"estAwardDate":"4/15/2015","Procurement Type":"New - Incumbent","Bid Type":"Standalone Contract"}]
and my d3 code:
// Various accessors that specify the four dimensions of data to visualize.
function x(d) { return d.date; }
function y(d) { return d.TotalEstSales; }
function radius(d) { return parseFloat(d.TotalEstSales);}
function color(d) { return d.region; }
function key(d) { return d.Title;}
// Chart dimensions.
var margin = {top: 19.5, right: 19.5, bottom: 19.5, left: 39.5},
width = 960 - margin.right,
height = 500 - margin.top - margin.bottom;
// Various scales. These domains make assumptions of data, naturally.
var xScale = d3.scale.log().domain([300, 1e5]).range([0, width]),
yScale = d3.scale.linear().domain([10000, 85000000]).range([height, 0]),
radiusScale = d3.scale.sqrt().domain([0, 5e8]).range([0, 40]),
colorScale = d3.scale.category10();
// The x & y axes.
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(12, d3.format(",d")),
yAxis = d3.svg.axis().scale(yScale).orient("left");
// Create the SVG container and set the origin.
var svg = d3.select("#chart").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 + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Add an x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width)
.attr("y", height - 6)
.text("Data of RFP");
// Add a y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 6)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Award amount");
// Add the year label; the value is set on transition.
var label = svg.append("text")
.attr("class", "year label")
.attr("text-anchor", "end")
.attr("y", height - 24)
.attr("x", width)
.text(2015);
// Load the data.
d3.json("rfpdata.json", function(data) {
// A bisector since many nation's data is sparsely-defined.
// var bisect = d3.bisector(function(d) { return d[0]; });
// Add a dot per nation. Initialize the data at 1800, and set the colors.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.style("fill", function(d) { return colorScale(color(d)); })
.call(position)
.sort(order);
// Add a title.
dot.append("title")
.text(function(d) { return d.Client; })
// Positions the dots based on data.
function position(dot) {
dot .attr("cx", function(d) { return xScale(x(d)); })
// .attr("cy", function(d) { return yScale(y(d)); })
.attr("r", function(d) { return radiusScale(radius(d)); });
}
// Defines a sort order so that the smallest dots are drawn on top.
function order(a, b) {
return radius(b) - radius(a);
}
// After the transition finishes, you can mouseover to change the year.
function enableInteraction() {
var yearScale = d3.scale.linear()
.domain([1800, 2009])
.range([box.x + 10, box.x + box.width - 10])
.clamp(true);
// Cancel the current transition, if any.
function mouseover() {
label.classed("active", true);
}
function mouseout() {
label.classed("active", false);
}
function mousemove() {
displayYear(yearScale.invert(d3.mouse(this)[0]));
}
}
// this is the function needed to bring in data
// Interpolates the dataset for the given (fractional) year.
function interpolateData(date) {
return data.map(function(d) {
return {
title: d.Title,
client: d.Client,
sales: parseFloat(d.TotalEstSales),
sales: interpolateValues(d.TotalEstSales, date),
};
});
}
// Finds (and possibly interpolates) the value for the specified year.
function interpolateValues(values, date) {
var i = bisect.left(values, date, 0, values.length - 1),
a = values[i];
if (i > 0) {
var b = values[i - 1],
t = (date - a[0]) / (b[0] - a[0]);
return a[1] * (1 - t) + b[1] * t;
}
return a[1];
}
});
I am not sure what I am doing wrong but the data is not displaying? Am i properly parsing the date string? This was a graph available on the d3 site. I want a bubble graph where the radius changes depending on the size of the sale and the date is on the x axis.
#all Update:
I was able to make the proper adjustment for date on the xaxis here:
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).tickFormat(d3.time.format("%m/%d")),
yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(23, d3.format(" ,d"));
d3.time.format was what I was looking for. Once data was loaded I needed to parse the date:
month = data.Date;
parseDate = d3.time.format("%m/%d/%Y").parse;
data.forEach(function(d) {
d.Date = parseDate(d.Date);
});
// update Dates here when new report comes in monthly
xScale.domain([parseDate("1/1/2015"),parseDate("6/1/2015")]);
obviously, using "Date" as a name column in the excel file was not idea for "Date" in js(because it is an oject).

Stacked bar chart in D3js - bars are not on the correct places

I'm trying to build a stacked bar chart in D3js. I have problems to set properly y and y0 attributes and draw the bars on their right positions. Probably I have a calculation mistake but I cannot find it. This is the link to the example code FIDDLE
The scenario is:
I group the data first by "period" and the periods are shown on xAxis
Then I have grouping by "type" - MONTH and ENTRY which should be stacked bars in different colors.
The sum "amount" for each type per each period is shown on yAxis.
I use nest function with 2 keys to structure the data. The problem appears when I draw the bars in the actual stacked bar chart. I'm not sure whether the problem is in the way I access the data (key and values) or in the way I set the attributes "y" and "height".
selection.selectAll("rect")
.data(function (d) { return d.values; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function (d) { return y(d.values); })
.attr("height", function (d) { return y(d.y0) + y(d.values); })
//.attr("height", function (d) { return y(d.y0) - y(d.values); })
.style("fill", function (d) { return color(d.key); })
The obvious errors are that one of the bars is hidden behind another one. And the second bar is under the xAxis.
I'm beginner in d3js and I cannot find the solution. Can somebody help me?
I can see a few things:
It looks like you're overcomplicating the nest. You should only need to nest a single level.
The max value that you're calculating will only ever be the maximum of a single element of the stack, when you actually want the maximum to be the total of the stack.
The group elements that you're creating (g), seem to be grouped the "wrong" way. You generally want to group the same "bit" of each stack. That is, you want the first rect of each stack to be in the same group as the other first rects. Then the second one in each stack will be grouped with the other second rects and so on. This is probably due to the nesting error in the first point.
You actually need to calculate the valueOffset, which you've got in your fiddle, but is commented out. This value is used to set the relative position when constructing the stack.
To help, I've put together what seems right based on what you've written. Check out the snippet below.
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 400 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var color = d3.scale.category10();
var data = [
{
"period":201409,
"type":"MONTH",
"amount":85.0
},
{
"period":201409,
"type":"ENTRY",
"amount":111.0
},
{
"period":201410,
"type":"MONTH",
"amount":85.0
},
{
"period":201410,
"type":"ENTRY",
"amount":55.0
}
];
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1, 0);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(10);
var svg = d3.select("#chart")
.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 + ")");
data.forEach(function(d) {
d["period"] = d["period"];
d["amount"] = +d["amount"];
d["type"] = d["type"];
});
var nest = d3.nest()
.key(function(d) { return d["type"];});
var dataByType = nest.entries(data);
//var max = d3.max(dataByGroup, function(d) { return d3.sum(d.values, function(e) { return e.values; }); })
//console.log("dataByGroup", dataByGroup);
var stack = d3.layout.stack()
.values(function(d) { return d.values; })
.x(function(d) { return d.period; })
.y(function(d) { return d.amount; })
.out(function(d, y0) {
d.valueOffset = y0;
});
//data: key: group element, values: values in each group
stack(dataByType);
var yMax = d3.max(dataByType, function(type) { return d3.max(type.values, function(d) { return d.amount + d.valueOffset; }); });
color.domain(dataByType[0].values.map(function(d) { return d.type; }));
x.domain(dataByType[0].values.map(function(d) { return d.period; }));
y.domain([0, yMax]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 3)
.attr("dy", ".71em")
.style("text-anchor", "end");
var selection = svg.selectAll(".group")
.data(dataByType)
.enter().append("g")
.attr("class", "group");
//.attr("transform", function(d) { return "translate(0," + y0(y0.domain()[0]) + ")"; });
selection.selectAll("rect")
.data(function (d) { return d.values; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("x", function(d) { return x(d.period); })
.attr("y", function (d) { return y(d.amount + d.valueOffset); })
.attr("height", function (d) { return y(d.valueOffset) - y(d.valueOffset + d.amount); })
.style("fill", function (d) { return color(d.type); })
.style("stroke", "grey");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
Some notes on the above snippet (that match my comments):
A much simpler nest:
var nest = d3.nest()
.key(function(d) { return d["type"];});
This is much simpler than your previous one, and there is no need to do the rollup function. Rollups are generally required when you want to aggregate your data, in this case you don't need to, which should be a giveaway that your nesting was too complex.
The calculation of the maximum value for the y axis:
var yMax = d3.max(dataByType, function(type) { return d3.max(type.values, function(d) { return d.amount + d.valueOffset; }); });
This will calculate the maximum value that your axis needs to take, making everything fit nicely.
If you look at the resulting SVG, you'll see what I mean about the grouping of the rects in each stack. I generally find that it's easier to group this way. I guess there's no "right" way, but this typically works best for me.
The calculation of the valueOffset in the stack:
d3.layout.stack()
.values(function(d) { return d.values; })
.x(function(d) { return d.period; })
.y(function(d) { return d.amount; })
.out(function(d, y0) {
d.valueOffset = y0;
});
The calculated valueOffset is used to "move" each rect in the stack into position relative to the other rects. You'll see it used a few times, calculating the max y value, the y attr of each rect, and the height of each rect.
I haven't explained every change that I've made, but hopefully with the above and the snippet you'll be able to work through the differences and apply it your exact use case.

Categories

Resources