7
$\begingroup$

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

a single series plot

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

multi series plot

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?

$\endgroup$
6
$\begingroup$

Update: Wrapping all in a function:

ClearAll[deviationslLLP]
deviationslLLP[ave_, dev_, opts: OptionsPattern[]] := Module[{fill = 
  Join @@ (Thread[Range[Length@ave] -> 
   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]]]

enter image description here


Original answer:

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]]

enter image description here

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

enter image description here

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

enter image description here

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

enter image description here

Add the option PlotStyle -> 63 to get

enter image description here

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

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]
$\endgroup$
  • $\begingroup$ 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) $\endgroup$ – Anti Earth Jan 19 '18 at 15:41
3
$\begingroup$

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[
          Thread[{dom, #}] &, 
          {#[[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[
   dataDevs : 
    Repeated[{{_?NumericQ, _?
        NumericQ, {Repeated[_?NumericQ, 2]}}, ___}],
   ops : OptionsPattern[]
   ] :=
  varianceListPlot[
   {#[[All, 1]], #[[All, 2]], #[[All, 3, 1]], #[[All, 3, 2]]} & /@ 
    dataDevs,
   ops
   ];
varianceListPlot[
   dd :
    {
     {__?NumericQ},
     {__?NumericQ},
     {__?NumericQ},
     {__?NumericQ}
     },
   ops : OptionsPattern[]
   ] :=
  varianceListPlot[{dd}, ops];
varianceListPlot[
  dataDevs : {{_?NumericQ, _?
      NumericQ, {Repeated[_?NumericQ, 2]}}, ___}, 
  ops : OptionsPattern[]
  ] :=
 varianceListPlot[{dataDevs}, ops]

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]
   }
  }
 ]

blahblah

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.