11
$\begingroup$

Mathematica 12 introduced a built-in support for logarithmic plots with error bars. A plot with default styling looks as expected:

ListLogPlot[
    Table[{i, Around[1 + Sin[i], 0.1 + 0.1 Cos[i]]}, {i, 0, 10, 1}],
    PlotRange -> {All, {0.01, 10}}
    ]

However, when I want to customize FenceWidth, this happens:

ListLogPlot[
    Table[{i, Around[1 + Sin[i], 0.1 + 0.1 Cos[i]]}, {i, 0, 10, 1.0}],
    PlotRange -> {All, {0.01, 10}},
    IntervalMarkersStyle -> <|"FenceWidth" -> 0.3|>
    ]

What is a proper way to set custom FenceWidth in logarithmic plot?

How to set FenceWidth in absolute units like in default styling, when the width of fences doesn't change when the plot is resized?

$\endgroup$
12
$\begingroup$

This is clearly a bug. In addition to the weird fence shape, the scaling behavior is also changed: fence widths change when the plot is resized.

A work-around is to post-process the output to straighten the fence lines and make fence widths independent of image size:

ClearAll[repairFences]
repairFences[w_] := Replace[#,  Line[x_] /; Length[Dimensions@x] ==  3 :>
  (Line[{Offset[{-w 72 /2 , 0}, #[[1,1]]], Offset[{w 72/2 , 0}, #[[1,1]]]}]& /@ 
    Partition[x, 2]), All] &

llp = ListLogPlot[Table[{i, Around[1 + Sin[i], 0.1 + 0.1 Cos[i]]}, {i, 0, 10,  1.0}],
       PlotRange -> {All, {0.01, 10}},
       IntervalMarkers -> "Fences",
       IntervalMarkersStyle -> <|"FenceWidth" -> .3|>
       ] ;

Row[Show[repairFences[.3] @ llp, ImageSize -> #]& /@ {200, 500}, Spacer[10]]

enter image description here

An alternative work-around is to remove IntervalMarkersStyle -> <|"FenceWidth" -> .3|> and post-process to modify the fence lines to desired length:

llp2 = ListLogPlot[Table[{i, Around[1 + Sin[i], 0.1 + 0.1 Cos[i]]}, {i, 0, 10, 1.0}],
     PlotRange -> {All, {0.01, 10}}, IntervalMarkers -> "Fences" ] 

Make fences 20 printer's points long (fence length is independent of image size):

llp2 /. Offset[{a_, _}, b_] :> Offset[{10 Sign[a] , 0}, b]

enter image description here

Make fences .3 user units long (fence length changes with image size):

llp2 /. Offset[{a_, _}, {b_, c_}] :> {b + .15 Sign[a], c}

enter image description here

You can still use IntervalMarkersStyle to style the fences:

llp3 = ListLogPlot[Table[{i, Around[1 + Sin[i], 0.1 + 0.1 Cos[i]]}, {i, 0, 10, 1.0}],
     PlotRange -> {All, {0.01, 10}}, 
     IntervalMarkers -> "Fences",
     IntervalMarkersStyle -> <|"FenceStyle" -> Red|>];

 llp3 /. Offset[{a_, _}, {b_, c_}] :> {b + .5 Sign[a], c}

enter image description here

Update: using the second approach to make a function:

ClearAll[modifyFenceWidth]
modifyFenceWidth[arg : h_[w_] | w_] := # /.  Switch[Head[arg], 
     Offset, Offset[{a_, _}, b_] :> Offset[{ w  Sign[a]/2 , 0}, b], 
     Real, Offset[{a_, _}, {b_, c_}] :> {b + w/2 Sign[a], c},
     Scaled, Offset[{a_, _}, {b_, c_}] :> 
       {b + w Abs[Subtract @@ PlotRange[#][[1]]]/2 Sign[a], c}] &

Examples:

you can specify the fence width in user units, printer's points or as fraction of the plot width:

Row[Labeled[modifyFenceWidth[#] @ llp2, 
     Style["modifyFenceWidth @ " <> ToString[#], 18], Top] & /@ 
   {.4, Offset @ 20, Scaled @ .1}, Spacer[10]]

enter image description here

$\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.