0
$\begingroup$

I'm working on visualizing critical strain data in Mathematica and would appreciate help plotting my data with error bars. Here's what I'm trying to achieve:

Plot the mean ± standard deviation of several realizations of critical strain as a function of z, on a list-line plot with error bars. Each dataset corresponds to a different value of system size N. I want to show a different curve for each N with its own color and marker (ideally rectangles), with error bars indicating the standard deviation at each value of z.

Here’s a minimal version of the data after parsing my CSV:

(* z-values *)
zValues = {2, 3, 4};

(* One block of 3 realizations of Compression and Shear data *)
compressionData1 = {
  {0.1, 0.2, 0.3},
  {0.12, 0.22, 0.31},
  {0.11, 0.19, 0.29}
};

shearData1 = {
  {0.05, 0.1, 0.15},
  {0.06, 0.09, 0.14},
  {0.04, 0.11, 0.16}
};

compressionData2 = {
  {0.2, 0.3, 0.4},
  {0.21, 0.31, 0.41},
  {0.19, 0.29, 0.39}
};

shearData2 = {
  {0.1, 0.15, 0.2},
  {0.11, 0.16, 0.21},
  {0.09, 0.14, 0.19}
};

blocks = {
  <|"N" -> 100, "Compression" -> compressionData1, 
    "Shear" -> shearData1|>,
  <|"N" -> 200, "Compression" -> compressionData2, 
    "Shear" -> shearData2|>
};

And my code to plot the mean (that works)

p1 = ListLinePlot[Table[Module[{meanCompression}, 
 meanCompression = Mean /@ Transpose[block["Compression"]];
 Callout[Transpose[{zValues, meanCompression}], 
 "N = " <> ToString[block["N"]]]], {block, blocks}], 
 PlotMarkers -> {Graphics[{Rectangle[{-0.5, -0.5}, {0.5, 0.5}]}], 0.03}, 
 PlotStyle -> {{Dashed}}, Frame -> True, 
 FrameLabel -> {"z", "Mean Critical Strain"}, ImageSize -> Large]

However when I try to add the errors bars whith the following code, I get an empty figure.

buildAroundData[zVals_, dataMatrix_] := Module[{means, stds},
  means = Mean /@ Transpose[dataMatrix];
  stds = StandardDeviation /@ Transpose[dataMatrix];
  Transpose[{zVals, MapThread[Around, {means, stds}]}]
]


p2 = ErrorListPlot[
   Table[{buildAroundData[zValues, block["Compression"]], 
      PlotStyle -> {ColorData[97][i], Dashed}, 
      PlotMarkers -> {Graphics[{EdgeForm[Black], 
          FaceForm[ColorData[97][i]], 
          Rectangle[{-0.5, -0.5}, {0.5, 0.5}]}], 0.03}, 
      PlotLegends -> "Expressions"}, {i, 
      Length[blocks]}, {block, {blocks[[i]]}}][[All, 1]], 
   Joined -> True, IntervalMarkers -> "Bands", Frame -> True, 
   FrameLabel -> {"z", "Mean Critical Compression Strain"}, 
   PlotLabel -> "Compression Strain vs z (All N)", 
   LabelStyle -> {FontFamily -> "Arial", FontSize -> 12}, 
   ImageSize -> Large];

Thank you so much for your help!

$\endgroup$
6
  • 1
    $\begingroup$ I'm not sure how to help because I don't understand your data or your objective. The length of zValues ({6,5,8,5,...}) seems to be 13, but that doesn't seem to correspond to the lengths of any of the other data, and yet your code does this comparison: Length[data[[i]]] == Length[zValues]. That's probably not crucial to your main question, but it is an impedance to trying to understand your code well enough to help you. $\endgroup$ Commented May 26 at 21:29
  • 1
    $\begingroup$ It would really be nice if you provided some sample data rather than a screenshot of a spreadsheet. If the data that you imported is in a form that is acceptable to you, then we don't really need to see the source csv data. So, please provide a reduced set of data (maybe zValues of length 3 and just one or two "blocks"). $\endgroup$ Commented May 26 at 21:31
  • 1
    $\begingroup$ Also, what should the lengths of the error bars be? Is it just the extreme values, i.e. the max/min of each row of transposed data before you take the mean? $\endgroup$ Commented May 26 at 21:33
  • 1
    $\begingroup$ You might even consider creating a "dummy" ListLinePlot that has the form that you're trying to achieve (can be a very reduced set of data, just something that provides a target form to validate against). $\endgroup$ Commented May 26 at 21:34
  • 2
    $\begingroup$ What I'd really like to be able to do is copy/paste your data, copy/paste your code, run to make sure I'm seeing what you're starting with, and then have a target (or at least some success criteria) to validate against. $\endgroup$ Commented May 26 at 21:37

1 Answer 1

0
$\begingroup$

Here is a more direct way to extract and transform the data you need. We can use Lookup to get the data of whichever type:

Lookup[blocks, "Compression"]
(* {{{0.1, 0.2, 0.3}, {0.12, 0.22, 0.31}, {0.11, 0.19, 0.29}}, 
    {{0.2, 0.3, 0.4}, {0.21, 0.31, 0.41}, {0.19, 0.29, 0.39}}} *)

We use an extended form of Lookup to apply a function:

Lookup[blocks, "Compression", Missing["Compression"], SomeFunction]
(* {SomeFunction[{{0.1, 0.2, 0.3}, {0.12, 0.22, 0.31}, {0.11, 0.19, 0.29}}],
    SomeFunction[{{0.2, 0.3, 0.4}, {0.21, 0.31, 0.41}, {0.19, 0.29, 0.39}}]} *)

That Missing["Compression"] bit is what to do if no data is found in the association. You can make that whatever you want, like maybe Null, but using Missing makes things very clear in cases of malformed data.

We want to apply a function that will get us an Around expression that uses Mean and StandardDeviation on the data. Here is one way that could look:

plotdata = 
  Lookup[
    blocks, 
    "Compression", 
    Missing["Compression"], 
    MapApply[Around]@*Transpose@*Comap[{Mean, StandardDeviation}]]

We're creating a composition (@*) of several functions. Comap applies multiple functions to the same argument. Transpose restructures just like you did. Then MapApply[Around] maps and applies (replaces the head) Around to each pair.

Now we can thread with zValues and Transpose to get pairs of data.

Transpose[Thread[{zValues, plotdata}, List, -1], 2 <-> 3]
(* {{{2, Around[0.11, 0.009999999999999995]}, 
     {3, Around[0.20333333333333337, 0.015275252316519466]}, 
     {4, Around[0.3, 0.010000000000000009]}}, 
    {{2, Around[0.20000000000000004, 0.009999999999999995]}, 
     {3, Around[0.3, 0.010000000000000009]}, 
     {4, Around[0.4000000000000001, 0.009999999999999981]}}} *)

This can be plotted directly.

ListLinePlot[Transpose[Thread[{zValues, plotdata}, List, -1], 2 <-> 3]]

enter image description here

The rest is decoration, and by the way I suggest that you don't put all of the plot decoration stuff into your data. Instead use the options for ListLinePlot.

ListLinePlot[
  Transpose[Thread[{zValues, test}, List, -1], 2 <-> 3],
  PlotLabels -> (StringForm["N = ``", #] & /@ Lookup[blocks, "N"]), 
  PlotMarkers -> {Graphics[{Rectangle[{-0.5, -0.5}, {0.5, 0.5}]}], 0.03}, 
  PlotStyle -> {{Dashed}}, 
  Frame -> True, 
  FrameLabel -> {"z", "Mean Critical Strain"}, 
  ImageSize -> Large]

enter image description here

Hope that gets you close enough so that you can extend it to exactly what you want. I don't know what you intended with ErrorListPlot--maybe you're using a different version or a package.

$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.