How to plot a simple stacked area chart

All I need to do is to plot a simple stacked chart (possibly also normalised).

I would like to start from these simple data:

a = [1,1,1,1.5,2,3]
b = [0.5,0.6,0.4,0.3,0.3,0.2]
c = [2,1.8,2.2,3.3,2.5,1.8]
sNames = ["a","b","c"]
xLabels = [2001,2002,2003,2004,2005,2006]

image

I did try:

  • PlotRecipes (now “GraphRecipes”) but the example given is too complex and it doesn’t run on Julia 1.1
  • Vega.jl. The example seems simple enought, but the package doesn’t work on Julia >= 0.7
  • VegaLite.jl. The example is very complex too, and I didn’t manage to reduce it to my simpler case
  • groupedbar recipe of StatsPlots, but it’s a histogram not a stacked area chart, and it is not indicated how to get it normalised (aside of course manually doing that ex-ante)

I have to say I’m a bit frustrated… I may just be dumb, I feel like in the domain of plotting there is a competition on showing cool features you can get with the various plotting packages, instead of showing simple examples that highlight one specific point at the time…

Here’s one way:

using Plots

@userplot StackedArea

# a simple "recipe" for Plots.jl to get stacked area plots
# usage: stackedarea(xvector, datamatrix, plotsoptions)
@recipe function f(pc::StackedArea)
    x, y = pc.args
    n = length(x)
    y = cumsum(y, dims=2)
    seriestype := :shape

    # create a filled polygon for each item
    for c=1:size(y,2)
        sx = vcat(x, reverse(x))
        sy = vcat(y[:,c], c==1 ? zeros(n) : reverse(y[:,c-1]))
        @series (sx, sy)
    end
end

a = [1,1,1,1.5,2,3]
b = [0.5,0.6,0.4,0.3,0.3,0.2]
c = [2,1.8,2.2,3.3,2.5,1.8]
sNames = ["a","b","c"]
x = [2001,2002,2003,2004,2005,2006]

plotly()
stackedarea(x, [a b c], labels=reshape(sNames, (1,3)))

stacked

1 Like

Thank you… I guess that if I want a “normalise” option I need to change the logic inside that function… but how to pass it a further, non-plots parameter ? like this?:

@recipe function f(pc::StackedArea; normalise=false)
[...]

The trick for VegaLite.jl (and presumably any grammar of graphics like package) is that you first need to get your data into a tidy format, and then can plot it.

If I start out putting this into a DataFrame:

using DataFrames

a = [1,1,1,1.5,2,3]
b = [0.5,0.6,0.4,0.3,0.3,0.2]
c = [2,1.8,2.2,3.3,2.5,1.8]
sNames = ["a","b","c"]
xLabels = [2001,2002,2003,2004,2005,2006]

df = DataFrame(year=xLabels, a=a, b=b, c=c)

then one can put it into tidy format easily with stack:

julia> df |> stack
18×3 DataFrame
│ Row │ variable │ value   │ year  │
│     │ Symbol   │ Float64 │ Int64 │
├─────┼──────────┼─────────┼───────┤
│ 1   │ a        │ 1.0     │ 2001  │
│ 2   │ a        │ 1.0     │ 2002  │
│ 3   │ a        │ 1.0     │ 2003  │
│ 4   │ a        │ 1.5     │ 2004  │
│ 5   │ a        │ 2.0     │ 2005  │
│ 6   │ a        │ 3.0     │ 2006  │
│ 7   │ b        │ 0.5     │ 2001  │
│ 8   │ b        │ 0.6     │ 2002  │
│ 9   │ b        │ 0.4     │ 2003  │
│ 10  │ b        │ 0.3     │ 2004  │
│ 11  │ b        │ 0.3     │ 2005  │
│ 12  │ b        │ 0.2     │ 2006  │
│ 13  │ c        │ 2.0     │ 2001  │
│ 14  │ c        │ 1.8     │ 2002  │
│ 15  │ c        │ 2.2     │ 2003  │
│ 16  │ c        │ 3.3     │ 2004  │
│ 17  │ c        │ 2.5     │ 2005  │
│ 18  │ c        │ 1.8     │ 2006  │

From there on it is pretty simple with VegaLite.jl. Here is the base version:

df |>
stack |>
@vlplot(:area, x=:year, y={:value, stack=:zero}, color="variable:n")

That gives me:

visualization%20(5)

And normalized:

df |>
stack |>
@vlplot(:area, x=:year, y={:value, stack=:normalize}, color="variable:n")

looks like this:

visualization%20(6)

4 Likes

Plots.jl now ships with the areaplot command.

4 Likes

Does anyone know how to make a recipe for a stacked area plot so that the PlotlyJS backend behavior resembles the figure created by the following code?

using PlotlyJS
function area()
    traces = [PlotlyJS.scatter(;x=1:3, y=[2, 1, 4], fill="tonexty", stackgroup = "one",),
    PlotlyJS.scatter(;x=1:3, y=[1, 1, 2], fill="tonexty", stackgroup = "one",),
    PlotlyJS.scatter(;x=1:3, y=[3, 0, 2], fill="tonexty", stackgroup = "one",)]

    PlotlyJS.plot(traces, PlotlyJS.Layout(title="stacked and filled line chart"))
end
area()