Chapter 05Stacks

When having multivariate data, it may be useful to show the data depicted as areas stacked on top of each other. Examples of this include stacked bar charts, stacked area graphs, and streamgraphs.

Consider the following array containing sales data for three fruits over three months.

var data = [
  {month: new Date(2018, 1, 1), apples: 10, bananas: 20, oranges: 15},
  {month: new Date(2018, 2, 1), apples: 15, bananas: 15, oranges: 15},
  {month: new Date(2018, 3, 1), apples: 20, bananas: 25, oranges: 15}
];

This dataset has a form that is typically visualized using a stacked chart or graph. The data for each fruit is referred to as a series. For example, the data for apples is the series (10, 15, 20). Each series can be displayed as an area, with one stacked on top of the other as shown in the visualization below. The number of objects in each series determines the width of the areas.

<script>
var data = [
 {month: new Date(2018, 1, 1), apples: 10, bananas: 20, oranges: 15},
 {month: new Date(2018, 2, 1), apples: 15, bananas: 15, oranges: 15},
 {month: new Date(2018, 3, 1), apples: 20, bananas: 25, oranges: 15}
];

var stackGen = d3.stack()
 .keys(["apples", "bananas", "oranges"]);
   
var stackedSeries = stackGen(data); 

var xScale = d3.scaleTime().domain([data[0].month, data[2].month]).range([50, 275]);
var yScale = d3.scaleLinear().domain([0,60]).range([275, 25]);

addAxis(d3.select("#demo0"), data, xScale, yScale, true); 

var colorScale = d3.scaleOrdinal()
 .domain(["apples", "bananas", "oranges"])
 .range(["red", "yellow", "orange"]);

var areaGen = d3.area()
 .x((d) => xScale(d.data.month))
 .y0((d) => yScale(d[0]))
 .y1((d) => yScale(d[1]));
   
d3.select("#demo0")
 .selectAll(".areas")
 .data(stackedSeries)
 .join("path")
 .attr("d", areaGen)
 .attr("fill", (d) => colorScale(d.key));
   
addLabels(d3.select("#demo0"), stackedSeries, areaGen);
</script>

<svg id="demo0" width= "300" height="300"></svg>
Figure 1. A stacked area graph.

In order to create stacked areas we need to compute cumulative y-values for each data point in each series. Let’s consider the stacked area graph shown above to see what we mean by cumulative values.

Notice that the visualization shows apples on the bottom, bananas in the middle, and oranges on top. Also notice that the bottom line of the apple area is the x-axis, and that the bottom line of the other two areas is the top line of the area below it. Therefore in order to define the areas we only need to compute the top lines of each area. Since the top lines are line segments drawn from points at each month mark on the x-axis, we only need to compute the value of the top line at these points.

Let’s start at the bottom of the graph and work our way up. Since the number of apples sold each month is depicted in the lower area, the y-coordinates of the points on the top line of the apple area are simply the values in the apples series. For the next layer, bananas, at each month point, the y-coordinate of the point on the top line of the bananas area is the y-coordinate of the point on the top line of the apples area plus the respective value in the bananas series. Similarly, at each time interval, the y-coordinate of the point on the top line of the oranges area is the y-coordinate of the point on the top line of the bananas area plus the respective value in the oranges series.

Thankfully, D3.js provides a stack generator that does this computation for us.

Creating Stacks

To create a stacked visualization using D3.js, we can use d3.stack, a method that returns a stack generator.

We may not want to create an area/layer for each series in the original dataset. Therefore, when using d3.stack, we need to specify which properties (series) in the dataset to use. We do so by using stack.keys. When we call d3.stack to create the generator, we’ll chain a call to stack.keys passing to it an array of the strings, where each string is the name of a property that we want to create a layer for.

For the dataset used in Figure 1, the keys are apples, bananas, and oranges, therefore to create a stack generator and assign the keys, we’ll do the following.

var stackGen = d3.stack()
  .keys(["apples", "bananas", "oranges"]);

After setting up the stack generator we invoke it, passing to it the dataset.

var stackedSeries = stackGen(data); 

When the stack generator is invoked with a dataset, it maps each series specified in stack.keys to a new series. Each new series contains the bottom and top y-coordinates for each data point in the original series, as well as reference to the object in the original dataset from which the top y-coordinate is computed.

The array referenced by stackedSeries holds the following data:

[
  [[0,  10, data[0]], [0,  15, data[1]], [0,  20, data[2]]],  //apples
  [[10, 30, data[0]], [15, 30, data[1]], [20, 45, data[2]]],  //bananas
  [[30, 45, data[0]], [30, 45, data[1]], [45, 60, data[2]]]   //oranges
]   

To render this data into stacked areas we need an area generator and some scales:

var xScale = d3.scaleTime()
  .domain([data[0].month, data[2].month])
  .range([50, 275]);
  
var yScale = d3.scaleLinear()
  .domain([0,60])
  .range([275, 25]);

var colorScale = d3.scaleOrdinal()
  .domain(["apples", "bananas", "oranges"])
  .range(["red", "yellow", "orange"]);

var areaGen = d3.area()
  .x((d) => xScale(d.data.month))
  .y0((d) => yScale(d[0]))
  .y1((d) => yScale(d[1]));

Now we can use the stackedSeries data with our areaGen to create multiple SVG paths which are filled with different colors.

d3.select("#demo1")
  .selectAll(".areas")
  .data(stackedSeries)
  .join("path")
  .attr("d", areaGen)
  .attr("fill", (d) => colorScale(d.key));
<script>
var data = [
 {month: new Date(2018, 1, 1), apples: 10, bananas: 20, oranges: 15},
 {month: new Date(2018, 2, 1), apples: 15, bananas: 15, oranges: 15},
 {month: new Date(2018, 3, 1), apples: 20, bananas: 25, oranges: 15}
];

var stackGen = d3.stack()
 .keys(["apples", "bananas", "oranges"]);
   
var stackedSeries = stackGen(data);

var xScale = d3.scaleTime().domain([data[0].month, data[2].month]).range([50, 275]);
var yScale = d3.scaleLinear().domain([0,60]).range([275, 25]);

var colorScale = d3.scaleOrdinal()
 .domain(["apples", "bananas", "oranges"])
 .range(["red", "yellow", "orange"]);

var areaGen = d3.area()
 .x((d) => xScale(d.data.month))
 .y0((d) => yScale(d[0]))
 .y1((d) => yScale(d[1]));
   
d3.select("#demo1")
 .selectAll(".areas")
 .data(stackedSeries)
 .join("path")
 .attr("d", areaGen)
 .attr("fill", (d) => colorScale(d.key));
   
</script>

<svg id="demo1" width= "300" height="300"></svg>
Figure 2. A basic stacked area graph.

Creating Bar Graphs

Instead of appending areas we can append SVG rects to create stacked bar graphs.

To do this, we will first bind each of the series in stackedSeries to a new g elements in our SVG. This gives us a g element for each fruit.

var sel = d3.select("#demo2")
  .select('g')
  .selectAll('g.series')
  .data(stackedSeries)
  .join('g')
  .classed('series', true)
  .style('fill', (d) => colorScale(d.key));

Recall that each series in stackedSeries is an array containing arrays where each inner array contains low and high y-coordinates.

Then, for each g element, we append multiple rect element, one for each pair of low and high y-coordinates.

sel.selectAll('rect')
  .data((d) => d)
  .join('rect')
  .attr('width', 40)
  .attr('y', (d) => yScale(d[1]))
  .attr('x', (d) => xScale(d.data.month) - 20)
  .attr('height', (d) => yScale(d[0]) -  yScale(d[1]));

Note that we use functions to generate our axis, labels, and areas in the example below and in many other examples on this page. The definitions of these functions can be found at the bottom of this page.

<script>
var data = [
  {month: new Date(2018, 1, 1), apples: 400, bananas: 200, cherries: 96, dates: 40},
  {month: new Date(2018, 2, 1), apples: 160, bananas: 150, cherries: 96, dates: 40},
  {month: new Date(2018, 3, 1), apples:  64, bananas:  96, cherries: 64, dates: 40},
  {month: new Date(2018, 4, 1), apples:  32, bananas:  48, cherries: 64, dates: 40}
];

var stack = d3.stack()
  .keys(["apples", "bananas", "cherries", "dates"]);
  
var stackedSeries = stack(data);

var xScale = d3.scaleTime().domain([data[0].month, data[3].month]).range([50,235]);
var yScale = d3.scaleLinear().domain([0, 650]).range([275,25]);
var colorScale = d3.scaleOrdinal()
  .domain(["apples", "bananas", "oranges", "cherries", "grapes", "dates"])
  .range(["red", "yellow", "orange", "pink", "purple", "brown"]);
        
//See end of page for addAxis() function definition
addAxis(d3.select("#demo2").append("g")
  .attr("transform", "translate(20,0)"), data, xScale, null, true);    //Adds in the X axis with ticks
addAxis(d3.select("#demo2"), data, null, yScale, true);                  //Adds in the Y axis
addAxis(d3.select("#demo2"), null, d3.scaleLinear().range([50,275]), null, true);  //Adds in a blank X axis
        
// Create a g element for each series
var sel = d3.select("#demo2")
  .select('g')
  .selectAll('g.series')
  .data(stackedSeries)
  .join('g')
  .classed('series', true)
  .style('fill', (d) => colorScale(d.key));

// For each series create a rect element for each month
sel.selectAll('rect')
  .data((d) => d)
  .join('rect')
  .attr('width', 40)
  .attr('y', (d) => yScale(d[1]))
  .attr('x', (d) => xScale(d.data.month) - 20)
  .attr('height', (d) => yScale(d[0]) -  yScale(d[1]));
</script>

<svg id="demo2" width="300" height="300"></svg>
Figure 3. A stacked bar graph.

Stack.value()

The stack.value method is used to retrieve the values associated with the keys from the data passed to the stack generator. By default, stack.value does not need to be called, as the keys are assumed to be properties of the objects in the array passed to the stack generator.

To see how stack.value can be used, consider the following data set:

var data = [
  {month: new Date(2018, 0, 1), 
   fruitSales: {
     apples: 400, 
     bananas: 200, 
     cherries: 96,  
     dates: 40, 
     oranges: 250, 
     grapes: 20}
  },
      ...
];

To create a stack generator with this dataset we call d3.stack and then set the keys to the names of the fruits as we did in the previous examples. We then call stack.value, passing it a lambda expression. The lambda expression has two parameters, obj and key, which correspond to an object in data and a key passed to stack.keys, respectively, and returns the value associated with the key in the object obj.

var stack = d3.stack()
  .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
  .value((obj, key) => obj.fruitSales[key]);
<script>
var data = [
  {
    month: new Date(2018, 0, 1), 
    fruitSales: {apples: 400, bananas: 200, cherries: 96,  dates: 40, oranges: 250, grapes: 20}
  },
  {
    month: new Date(2018, 1, 1), 
    fruitSales: {apples: 160, bananas: 150, cherries: 96,  dates: 40, oranges: 200, grapes: 25}
  },
  {
    month: new Date(2018, 2, 1), 
    fruitSales: {apples:  64, bananas:  96, cherries: 64,  dates: 40, oranges: 150, grapes: 30}
  },
  {
    month: new Date(2018, 3, 1), 
    fruitSales: {apples:  32, bananas:  48, cherries: 64,  dates: 40, oranges: 100, grapes: 20}
  },
  {
    month: new Date(2018, 4, 1), 
    fruitSales: {apples:  40, bananas: 100, cherries: 64,  dates: 40, oranges: 115, grapes: 45}
  },
  {
    month: new Date(2018, 5, 1), 
    fruitSales: {apples: 100, bananas: 250, cherries: 86,  dates: 40, oranges: 225, grapes: 50}
  },
  {
    month: new Date(2018, 6, 1), 
    fruitSales: {apples: 150, bananas: 125, cherries: 96,  dates: 40, oranges: 200, grapes: 15}
  },
  {
    month: new Date(2018, 7, 1), 
    fruitSales: {apples: 100, bananas:  75, cherries: 106, dates: 40, oranges: 210, grapes: 10}
  }
];

var stack = d3.stack()
  .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
  .value((obj, key) => obj.fruitSales[key]);

var stackedSeries = stack(data);

var xScale = d3.scaleTime().range([50,275]);
var yScale = d3.scaleLinear().range([275,25]);
var colorScale = d3.scaleOrdinal()
  .domain(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
  .range(["red", "yellow", "pink", "brown", "orange", "purple"]);

addAxis(d3.select("#demo3"), data, xScale, yScale, false);

var area = d3.area()
  .x((d) => xScale(d.data.month))
  .y0((d) => yScale(d[0]))
  .y1((d) => yScale(d[1]))
  .curve(d3.curveBasis);

d3.select("#demo3")
  .selectAll('.areas')
  .data(stackedSeries)
  .join('path')
  .attr('d', area)
  .attr("fill", (d) => colorScale(d.key));
    
addLabels(d3.select("#demo3"), stackedSeries, area);
</script>

<svg id="demo3" width="300" height="300"></svg>
Figure 4. Stacked graph using stack.value().

Stack Ordering

By setting the .order([order]) accessor of a stack we can change where each series appears in the stack. The default ordering if one is not set is d3.stackOrderNone.

<script>
    var data = [
        {month: new Date(2018, 0, 1), fruitSales: {apples: 400, bananas: 200, cherries: 96,  dates: 40, oranges: 250, grapes: 20}},
        {month: new Date(2018, 1, 1), fruitSales: {apples: 160, bananas: 150, cherries: 96,  dates: 40, oranges: 200, grapes: 25}},
        {month: new Date(2018, 2, 1), fruitSales: {apples:  64, bananas:  96, cherries: 64,  dates: 40, oranges: 150, grapes: 30}},
        {month: new Date(2018, 3, 1), fruitSales: {apples:  32, bananas:  48, cherries: 64,  dates: 40, oranges: 100, grapes: 20}},
        {month: new Date(2018, 4, 1), fruitSales: {apples:  40, bananas: 100, cherries: 64,  dates: 40, oranges: 115, grapes: 45}},
        {month: new Date(2018, 5, 1), fruitSales: {apples: 100, bananas: 250, cherries: 86,  dates: 40, oranges: 225, grapes: 50}},
        {month: new Date(2018, 6, 1), fruitSales: {apples: 150, bananas: 125, cherries: 96,  dates: 40, oranges: 200, grapes: 15}},
        {month: new Date(2018, 7, 1), fruitSales: {apples: 100, bananas:  75, cherries: 106, dates: 40, oranges: 210, grapes: 10}}
    ];

    var xScale = d3.scaleTime().domain([data[0].month, data[data.length - 1].month]).range([50,275]);
    var yScale = d3.scaleLinear().domain([0,1000]).range([275,25]);
    var colorScale = d3.scaleOrdinal()
        .domain(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
        .range(["red", "yellow", "pink", "brown", "orange", "purple"]);

    var stack1 = d3.stack() 
            .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderNone)
            .offset(d3.stackOffsetNone);
    var stackedSeries1 = stack1(data);

    var stack2 = d3.stack()
            .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderReverse)
            .offset(d3.stackOffsetNone);
    var stackedSeries2 = stack2(data);

    var area = d3.area()
        .x((d) => xScale(d.data.month))
        .y0((d) => yScale(d[0]))
        .y1((d) => yScale(d[1]))
        .curve(d3.curveBasis);
                
    addAreas(d3.select("#demo4n"), stackedSeries1, area); // Areas to stackOrderNone
    addAreas(d3.select("#demo4r"), stackedSeries2, area); // Areas to stackOrderReverse
     
    addLabels(d3.select("#demo4n"), stackedSeries1, area) // Labels to stackOrderNone
    addLabels(d3.select("#demo4r"), stackedSeries2, area) // Labels to stackOrderReverse
</script>

<svg id="demo4n" width="300" height="300"></svg>
<svg id="demo4r" width="300" height="300"></svg>
Figure 5. .
<script>
        var data = [
            {month: new Date(2018, 0, 1), fruitSales: {apples: 400, bananas: 200, cherries: 96,  dates: 40, oranges: 250, grapes: 20}},
            {month: new Date(2018, 1, 1), fruitSales: {apples: 160, bananas: 150, cherries: 96,  dates: 40, oranges: 200, grapes: 25}},
            {month: new Date(2018, 2, 1), fruitSales: {apples:  64, bananas:  96, cherries: 64,  dates: 40, oranges: 150, grapes: 30}},
            {month: new Date(2018, 3, 1), fruitSales: {apples:  32, bananas:  48, cherries: 64,  dates: 40, oranges: 100, grapes: 20}},
            {month: new Date(2018, 4, 1), fruitSales: {apples:  40, bananas: 100, cherries: 64,  dates: 40, oranges: 115, grapes: 45}},
            {month: new Date(2018, 5, 1), fruitSales: {apples: 100, bananas: 250, cherries: 86,  dates: 40, oranges: 225, grapes: 50}},
            {month: new Date(2018, 6, 1), fruitSales: {apples: 100, bananas: 125, cherries: 96,  dates: 40, oranges: 200, grapes: 15}},
            {month: new Date(2018, 7, 1), fruitSales: {apples: 100, bananas:  75, cherries: 106, dates: 40, oranges: 210, grapes: 10}}
        ];
       
        var xScale = d3.scaleTime().domain([data[0].month,data[data.length -1].month]).range([50,275]);
        var yScale = d3.scaleLinear().domain([0,1000]).range([275,25]);
        var colorScale = d3.scaleOrdinal()
            .domain(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .range(["red", "yellow", "pink", "brown", "orange", "purple"]);
            
        var stack1 = d3.stack()
                .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
                .value((d, key) => d.fruitSales[key])
                .order(d3.stackOrderAscending)
                .offset(d3.stackOffsetNone);
        var stackedSeries1 = stack1(data);

        var stack2 = d3.stack()
                .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
                .value((d, key) => d.fruitSales[key])
                .order(d3.stackOrderDescending)
                .offset(d3.stackOffsetNone);
        var stackedSeries2 = stack2(data);

        var area = d3.area()
            .x((d) => xScale(d.data.month))
            .y0((d) => yScale(d[0]))
            .y1((d) => yScale(d[1]))
            .curve(d3.curveBasis);
        
        addAreas(d3.select("#demo5a"), stackedSeries1, area); // Areas to stackOrderAscending
        addAreas(d3.select("#demo5d"), stackedSeries2, area); // Areas to stackOrderDescending 
        
        addLabels(d3.select("#demo5a"), stackedSeries1, area); // Add Labels to stackOrderAcsending
        addLabels(d3.select("#demo5d"), stackedSeries2, area); // Add Labels to stackOrderDescending
   </script>

   <svg id="demo5a" width="300" height="300"></svg>
   <svg id="demo5d" width="300" height="300"></svg>
Figure 6. .

d3.stackOrderAppearance( and d3.stackOrderInsideOut()

When having stacks with large amount of data, readability is important. d3.stackOrderAppearance() and d3.stackOrderInsideOut() can be used to improve readability in our stacks. The ways that d3.stackOrderAppearance() and d3.stackOrderInsideOut() sort are further explained and reasoned in Stacked Graphs—Geometry & Aesthetics by Byron & Wattenberg.

Below is an graphic explanation on how these orderings sort our data.

First d3.stackOrderAppearance() and d3.stackOrderInsideOut() finds at what index each series has its maximum value.

Index Apples Bananas Oranges Grapes
01111
110111
225111
310111
411110
511125
611110
711101
811251
911101
1011011
1112511
1211011
131111
Index at
Max Value
Apples: 2 Bananas: 11 Oranges: 8 Grapes: 5

Next, the orderings create an array of the indices of its maximum value.

Apples Bananas Oranges Grapes
Indices 2 11 8 5

Finally those indices are sorted least to greatest:

Apples Grapes Oranges Bananas
Indices 2 5 8 11
------------------------------------------------------->
<script>
    var data = [
   		{month: new Date(2018, 0, 1),  fruitSales: {apples: 1,    bananas: 1,   oranges: 1,   grapes: 1}},
        {month: new Date(2018, 1, 1),  fruitSales: {apples: 10,   bananas: 1,   oranges: 1,   grapes: 1}},
        {month: new Date(2018, 2, 1),  fruitSales: {apples: 25,   bananas: 1,   oranges: 1,   grapes: 1}},
        {month: new Date(2018, 3, 1),  fruitSales: {apples: 10,   bananas: 1,   oranges: 1,   grapes: 1}},
        {month: new Date(2018, 4, 1),  fruitSales: {apples: 1,    bananas: 1,  oranges: 1,   grapes: 10}},
        {month: new Date(2018, 5, 1),  fruitSales: {apples: 1,    bananas: 1,  oranges: 1,   grapes: 25}},
        {month: new Date(2018, 6, 1),  fruitSales: {apples: 1,    bananas: 1,  oranges: 1,   grapes: 10}},
        {month: new Date(2018, 7, 1),  fruitSales: {apples: 1,    bananas: 1,   oranges: 10,  grapes: 1}},
        {month: new Date(2018, 8, 1),  fruitSales: {apples: 1,    bananas: 1,   oranges: 25,  grapes: 1}},
        {month: new Date(2018, 9, 1),  fruitSales: {apples: 1,    bananas: 1,   oranges: 10,  grapes: 1}},
        {month: new Date(2018, 10, 1), fruitSales: {apples: 1,    bananas: 10,   oranges: 1,   grapes: 1}},
        {month: new Date(2018, 11, 1), fruitSales: {apples: 1,    bananas: 25,   oranges: 1,   grapes: 1}},
        {month: new Date(2019, 0, 1),  fruitSales: {apples: 1,    bananas: 10,   oranges: 1,   grapes: 1}},
        {month: new Date(2019, 1, 1),  fruitSales: {apples: 1,    bananas: 1,   oranges: 1,   grapes: 1}}
    ];

    var xScale = d3.scaleLinear().domain([data[0].month, data[data.length-1].month]).range([10,290]);
    var yScale = d3.scaleLinear().domain([0,30]).range([175,25]);
    var colorScale = d3.scaleOrdinal()
        .domain(["apples", "bananas", "oranges", "grapes"])
        .range(["red", "yellow", "orange", "purple"]);

    var stack1 = d3.stack()
            .keys(["apples", "bananas", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderAppearance)
            .offset(d3.stackOffsetNone);
    var stackedSeries1 = stack1(data);

    var stack2 = d3.stack()
            .keys(["apples", "bananas", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderInsideOut)
            .offset(d3.stackOffsetWiggle);
    var stackedSeries2 = stack2(data);

    var area1 = d3.area()
        .x((d) => xScale(d.data.month))
        .y0((d) => yScale(d[0]))
        .y1((d) => yScale(d[1]))
        .curve(d3.curveBasis);
    var area2 = d3.area()
        .x((d) => xScale(d.data.month))
        .y0((d) => yScale(d[0] + 25/2))
        .y1((d) => yScale(d[1] + 25/2))
        .curve(d3.curveBasis);

    addAreas(d3.select("#demo6a"), stackedSeries1, area1); // Areas to stackOrderAppearance
    addAreas(d3.select("#demo6i"), stackedSeries2, area2); // Areas to stackOrderInsideOut
        
    addLabels(d3.select("#demo6a"), stackedSeries1, area1); // Labels to stackOrderAppearance
    addLabels(d3.select("#demo6i"), stackedSeries2, area2); // Labels to stackOrderInsideOut
</script>

<svg id="demo6a" class="svgClass" width="300" height="200"></svg>
<svg id="demo6i" class="svgClass" width="300" height="200"></svg>
Figure 7. .

Stack Offsets

By setting the .offset([offset]) we can control the baselines that our stacks use. The baseline for the default offset is 0, so every stack bottoms out at zero and works its way up.

<script>
    var data = [
        {month: new Date(2018, 0, 1), fruitSales: {apples: 400, bananas: 200, cherries: 96,  dates: 40, oranges: 250, grapes: 20}},
        {month: new Date(2018, 1, 1), fruitSales: {apples: 160, bananas: 150, cherries: 96,  dates: 40, oranges: 200, grapes: 25}},
        {month: new Date(2018, 2, 1), fruitSales: {apples:  64, bananas:  96, cherries: 64,  dates: 40, oranges: 150, grapes: 30}},
        {month: new Date(2018, 3, 1), fruitSales: {apples:  32, bananas:  48, cherries: 64,  dates: 40, oranges: 100, grapes: 20}},
        {month: new Date(2018, 4, 1), fruitSales: {apples:  40, bananas: 100, cherries: 64,  dates: 40, oranges: 115, grapes: 45}},
        {month: new Date(2018, 5, 1), fruitSales: {apples: 100, bananas: 250, cherries: 86,  dates: 40, oranges: 225, grapes: 50}},
        {month: new Date(2018, 6, 1), fruitSales: {apples: 150, bananas: 125, cherries: 96,  dates: 40, oranges: 200, grapes: 15}},
        {month: new Date(2018, 7, 1), fruitSales: {apples: 100, bananas:  75, cherries: 106, dates: 40, oranges: 210, grapes: 10}}
    ];

    var xScale = d3.scaleTime().domain([data[0].month, data[data.length - 1].month]).range([50,275]);
    var yScaleNone = d3.scaleLinear().domain([0,1000]).range([275,25]);
    var yScaleExpand = d3.scaleLinear().domain([0,1]).range([275,25]);
    var colorScale = d3.scaleOrdinal()
        .domain(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
        .range(["red", "yellow", "pink", "brown", "orange", "purple"]);

    var stack1 = d3.stack() 
            .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderNone)
            .offset(d3.stackOffsetNone);
    var stackedSeries1 = stack1(data);

    var stack2 = d3.stack()
            .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderNone)
            .offset(d3.stackOffsetExpand);
    var stackedSeries2 = stack2(data);

    var areaNone = d3.area()
        .x((d) => xScale(d.data.month))
        .y0((d) => yScaleNone(d[0]))
        .y1((d) => yScaleNone(d[1]))
        .curve(d3.curveBasis);
        
    var areaExpanding = d3.area()
        .x((d) => xScale(d.data.month))
        .y0((d) => yScaleExpand(d[0]))
        .y1((d) => yScaleExpand(d[1]))
        .curve(d3.curveBasis);
    
    addAreas(d3.select("#demo7n").select("#stack"), stackedSeries1, areaNone); // Areas to stackOffsetNone
    addAreas(d3.select("#demo7e").select("#stack"), stackedSeries2, areaExpanding); // Areas to stackOffsetExpanding
        
    addLabels(d3.select("#demo7n").select("#stack"), stackedSeries1, areaNone); // Labels to stackOffsetNone
    addLabels(d3.select("#demo7e").select("#stack"), stackedSeries2, areaExpanding); // Labels to stackOffsetExpanding
</script>

<svg id="demo7n" width="300" height="300">
  <g id="stack"></g>
  <g id="baseline">
      <text y="290">Baseline</text>
      <path d="M 25 275 l 325 0" stroke="green" stroke-width="3px"></path>
  </g>
</svg>
<svg id="demo7e" width="300" height="300">
  <g id="stack"></g>
  <g id="baseline">
  		<text x="" y="290">Baseline</text>
        <text x="290" y="270">0</text>
        <text x="290" y="40">1</text>
        <path d="M 25 25 l 325 0" stroke="green" stroke-width="3px"></path>
        <path d="M 25 275 l 325 0" stroke="green" stroke-width="3px"></path>
  </g>
</svg>
Figure 8. .
<script>
    var data = [
        {month: new Date(2018, 0, 1), fruitSales: {apples: 400, bananas: 200, cherries: 96,  dates: 40, oranges: 250, grapes: 20}},
        {month: new Date(2018, 1, 1), fruitSales: {apples: 160, bananas: 150, cherries: 96,  dates: 40, oranges: 200, grapes: 25}},
        {month: new Date(2018, 2, 1), fruitSales: {apples:  64, bananas:  96, cherries: 64,  dates: 40, oranges: 150, grapes: -30}},
        {month: new Date(2018, 3, 1), fruitSales: {apples:  32, bananas:  48, cherries: 64,  dates: 40, oranges: 100, grapes: -20}},
        {month: new Date(2018, 4, 1), fruitSales: {apples:  40, bananas: 100, cherries: 64,  dates: 40, oranges: 115, grapes: -45}},
        {month: new Date(2018, 5, 1), fruitSales: {apples: 100, bananas: -25, cherries: 86,  dates: 40, oranges: -225, grapes: 50}},
        {month: new Date(2018, 6, 1), fruitSales: {apples: 150, bananas: -125, cherries: 96,  dates: 40, oranges: -200, grapes: 15}},
        {month: new Date(2018, 7, 1), fruitSales: {apples: 100, bananas:  -75, cherries: 106, dates: 40, oranges: -210, grapes: 10}}
    ];

    var xScale = d3.scaleTime().domain([data[0].month, data[data.length - 1].month]).range([50+225/data.length/2, 275 - 225/data.length/2]);
    var yScaleDiv = d3.scaleLinear().domain([-1000,1000]).range([275,25]);
    var colorScale = d3.scaleOrdinal()
        .domain(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
        .range(["red", "yellow", "pink", "brown", "orange", "purple"]);

    var stack = d3.stack() 
            .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderNone)
            .offset(d3.stackOffsetDiverging);
    var stackedSeries = stack(data);

	addAxis(d3.select("#demo8d"), data, xScale, yScaleDiv);
	addAxis(d3.select("#demo8d"), null, d3.scaleTime().range([50,275]), null);

    // Create a g element for each series
    var g = d3.select("#demo8d")
        .select('#stack')
        .selectAll('g.series')
        .data(stackedSeries)
        .join('g')
        .classed('series', true)
        .style('fill', (d) => colorScale(d.key));

    // For each series create a rect element for each month
    g.selectAll('rect')
        .data((d) => d)
        .join('rect')
        .attr('width', 225/data.length)
        .attr('y', (d) => yScaleDiv(d[1]))
        .attr('x', (d, i) => i * (225/data.length) + 50)
        .attr('height', (d) => yScaleDiv(d[0]) -  yScaleDiv(d[1])); 
        
    //Adds in the baseline
    d3.select("#baseline8").append("path").attr("d", "M 25 " + yScaleDiv(0) + " l 325 0").attr("stroke", "green").attr("stroke-width", "5px");
    d3.select("#baseline8").append("text").attr("x", 0).attr("y", yScaleDiv(0) - 10).text("Baseline");
        
</script>

<svg id="demo8d" width="300" height="300">
    <g id="stack"></g>
    <g id="baseline8"></g>
</svg>
Figure 9. .
<script>
    var data = [
        {month: new Date(2018, 0, 1), fruitSales: {apples: 400, bananas: 200, cherries: 96,  dates: 40, oranges: 250, grapes: 20}},
        {month: new Date(2018, 1, 1), fruitSales: {apples: 160, bananas: 150, cherries: 96,  dates: 40, oranges: 200, grapes: 25}},
        {month: new Date(2018, 2, 1), fruitSales: {apples:  64, bananas:  96, cherries: 64,  dates: 40, oranges: 150, grapes: 30}},
        {month: new Date(2018, 3, 1), fruitSales: {apples:  32, bananas:  48, cherries: 64,  dates: 40, oranges: 100, grapes: 20}},
        {month: new Date(2018, 4, 1), fruitSales: {apples:  40, bananas: 100, cherries: 64,  dates: 40, oranges: 115, grapes: 45}},
        {month: new Date(2018, 5, 1), fruitSales: {apples: 100, bananas: 250 , cherries: 86,  dates: 40, oranges: 225, grapes: 50}},
        {month: new Date(2018, 6, 1), fruitSales: {apples: 150, bananas: 125, cherries: 96,  dates: 40, oranges: 200, grapes: 15}},
        {month: new Date(2018, 7, 1), fruitSales: {apples: 100, bananas:  75, cherries: 106, dates: 40, oranges: 210, grapes: 10}}
    ];

    var xScale = d3.scaleTime().domain([data[0].month, data[data.length - 1].month]).range([50,275]);
    var yScale = d3.scaleLinear().domain([0,1000]).range([275,25]);
    var colorScale = d3.scaleOrdinal()
        .domain(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
        .range(["red", "yellow", "pink", "brown", "orange", "purple"]);

    var stack1 = d3.stack() 
            .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderNone)
            .offset(d3.stackOffsetSilhouette);
    var stackedSeries1 = stack1(data);

    var stack2 = d3.stack()
            .keys(["apples", "bananas", "cherries", "dates", "oranges", "grapes"])
            .value((d, key) => d.fruitSales[key])
            .order(d3.stackOrderInsideOut)
            .offset(d3.stackOffsetWiggle);
    var stackedSeries2 = stack2(data);

    var area = d3.area()
        .x((d) => xScale(d.data.month))
        .y0((d) => yScale(d[0]))
        .y1((d) => yScale(d[1]))
        .curve(d3.curveBasis);
    
    addAreas(d3.select("#demo9s").select("#stack9s"), stackedSeries1, area, "translate(0, -150)"); // Areas to stackOffsetSilhouette
    addAreas(d3.select("#demo9w").select("#stack9w"), stackedSeries2, area); // Areas to stackOrderReverse // Areas to stackOffsetWiggle
    
    addLabels(d3.select("#demo9s").select("#stack9s").append("g").attr("transform", "translate(0, -150)"), stackedSeries1, area); // Labels to stackOffsetSilhouette
    addLabels(d3.select("#demo9w").select("#stack9w"), stackedSeries2, area); // Labels to stackOffsetWiggle

    //Adds the baseline the Silhouette
	d3.select("#baseline9s").append("path").attr("d", "M 25 " + yScale(0) + " l 325 0").attr("stroke", "green").attr("stroke-width", "5px");
    d3.select("#baseline9s").append("text").attr("x", 0).attr("y", yScale(0) - 10).text("Baseline");
</script>

<svg id="demo9s" width="300" height="300">
  <g id="stack9s"></g>
  <g id="baseline9s" transform="translate(0,-150)")></g>
</svg>
<svg id="demo9w" width="300" height="300">
  <g id="stack9w"></g>
</svg>
Figure 10. .