# Shade a ListLinePlot of many series with their deviations

Consider a single list of data avg = {.5, .6, .5,...} with corresponding standard deviations dev = {.1, .2, .2, ...}.

I use some pretty verbose code...

ListLinePlot[
{avg, avg + dev, avg - dev},
PlotStyle -> {Blue, None, None},
Filling -> {1 -> {2}, 1 -> {3}}
]


to create a plot like this

which shows the data (the blue line) and shades the region spanned by 2x standard deviations of each datum (like a continuous error bar).

There are clearly problems with this method, chiefly that I had to manually specify the colour palette (Blue in PlotStyle).

This becomes a pain when I want to present multiple series. Imagine I had list-of-lists avgs = {{.5, ...}, {.6, ...}} and similarly structured standard deviations devs. To plot the first 3 of my series, I can do

ListLinePlot[
{
avgs[[1]], avgs[[1]] + devs[[1]], avgs[[1]] - devs[[1]],
avgs[[2]], avgs[[2]] + devs[[2]], avgs[[2]] - devs[[2]],
avgs[[3]], avgs[[3]] + devs[[3]], avgs[[3]] - devs[[3]]
},
PlotStyle -> {
Blue, None, None,
Orange, None, None,
Red, None, None
},
Filling -> {
1 -> {2}, 1 -> {3},
4 -> {5}, 4 -> {6},
7 -> {8}, 7 -> {9}
}
]


to produce

I now need to plot about 30 series like this!

While it's easy (albeit seemingly unnecessary) to create the first and third arguments to ListLinePlot programmatically, constructing PlotStyle in this way is painful for a general number of series.

So; is there a less-crude way to go about this? Could I otherwise get some help in automating PlotStyle to use/wrap-around a colour palette?

Update: Wrapping all in a function:

ClearAll[deviationslLLP]
deviationslLLP[ave_, dev_, opts: OptionsPattern[]] := Module[{fill =
List/@(Length[ave] # + Range[Length@ave])]& /@ {1, 2}),
apd = Style[#, Opacity[0]] & /@ (ave + dev),
amd = Style[#, Opacity[0]] & /@ (ave - dev)},
ListLinePlot[Join @@ {ave, apd, amd}, Filling -> fill, opts]]


Examples:

dt = RandomVariate[NormalDistribution[1, .05], {10, 50}];
averages = Mean[# dt] & /@ Range[10];
sdevs = StandardDeviation[# dt] & /@ Range[10];

deviationslLLP[averages, sdevs, Frame -> True, ImageSize -> 500,
PlotLegends -> RandomWord["Noun", 10]]]


You can wrap ave - dev and ave + dev pieces of your data with Style[#, Opacity[0]]& to avoid using PlotStyle:

dt = RandomVariate[NormalDistribution[1, .1], {10, 50}];
averages = Mean[# dt] & /@ {1, 2, 3};
sdevs = StandardDeviation[# dt] & /@ {1, 2, 3};
aplussd = Style[#, Opacity[0]]&/@(averages + sdevs);
aminussd = Style[#, Opacity[0]]&/@(averages - sdevs);
fillings[n_] := Join @@ (Thread[Range[n] -> List/@(n # + Range[n])]& /@ {1, 2});

ListLinePlot[Join@@{averages, aplussd, aminussd}, Filling -> fillings[Length @ averages]]


If you wish yo use PlotStyle you can to specify three colors, say, PlotStyle->{Red, Green,Purple}] to get

Or PlotStyle->{Automatic, Green,Purple}] to get

Use NormalDistribution[1, .05] instead of NormalDistribution[1, .1] and Range[10] instead of {1,2,3} to get

Add the option PlotStyle -> 63 to get

• This is exactly what I'm looking for (though I've had a hard time controlling evaluation order when wrapping this in a function) Commented Jan 19, 2018 at 16:55
• @AntiEarth, please see the update.
– kglr
Commented Jan 19, 2018 at 18:01

Maybe use a function like makePlot in MapThread:

makePlot = Function[{avg, dev, col},
ListLinePlot[{avg, avg - dev, avg + dev},
PlotStyle -> {col, None, None}, Filling -> {1 -> {2}, 1 -> {3}}]];

plots = MapThread[makePlot, {avgs, devs, {Blue, Orange, Red}}];

Show[plots]

• Automating the args is the easy part; I'm looking for help in not having to explicitly specify a fixed-length colour palette (also using Show introduces new problems; I'd prefer to avoid it) Commented Jan 19, 2018 at 15:41

I have the sense that this has been answered before, but a quick look couldn't find it, so here's my quick take on it:

ClearAll[varianceListPlot];
Options[varianceListPlot] =
Options[ListLinePlot];
varianceListPlot[
domMainLowHigh :
{
Repeated[
{
{__?NumericQ},
{__?NumericQ},
{__?NumericQ},
{__?NumericQ}
}
]
},
ops : OptionsPattern[]
] /; Length[DeleteDuplicates[Map[Length, domMainLowHigh, {2}]]] ==
1 :=
With[
{
plots =
Join @@
Map[
With[{dom = #[[1]]},
Map[
{#[[2]], #[[2]] + #[[3]], #[[2]] - #[[4]]}
]
] &,
domMainLowHigh
],
ps = Replace[OptionValue[PlotStyle], Automatic :> ColorData[97]]
},
ListLinePlot[
plots,
PlotStyle ->
Table[
If[Mod[i, 3] == 1,
If[ListQ@ps,
ps[[Mod[Floor[i/3] + 1, Length@ps, 1]]],
Replace[ps[Floor[i/3] + 1], _ps :> ps]
],
None
],
{i, Length@plots}
],
ops,
Filling ->
Flatten@
Table[
{
i -> {i + 1},
i -> {i + 2}
},
{i, 1, Length@plots, 3}
]
]
];
varianceListPlot[
Repeated[{{_?NumericQ, _?
NumericQ, {Repeated[_?NumericQ, 2]}}, ___}],
ops : OptionsPattern[]
] :=
varianceListPlot[
{#[[All, 1]], #[[All, 2]], #[[All, 3, 1]], #[[All, 3, 2]]} & /@
ops
];
varianceListPlot[
dd :
{
{__?NumericQ},
{__?NumericQ},
{__?NumericQ},
{__?NumericQ}
},
ops : OptionsPattern[]
] :=
varianceListPlot[{dd}, ops];
varianceListPlot[
NumericQ, {Repeated[_?NumericQ, 2]}}, ___},
ops : OptionsPattern[]
] :=


I provided a two syntaxes; either you pass the domain, main plot, upper variance, and lower variance as a 4-tuple for each data set or you can do it point-wise as {x, y, {above, below}} and it'll unwrap to the prior syntax.

varianceListPlot[
{
{
dom,
Sin[dom],
ConstantArray[1, Length@dom],
ConstantArray[1, Length@dom]
},
{
dom,
Cos[dom],
ConstantArray[1, Length@dom],
ConstantArray[1, Length@dom]
},
{
dom,
3*Cos[dom/π],
ConstantArray[1, Length@dom],
ConstantArray[1, Length@dom]
}
}
]