2
$\begingroup$

The page "How to | Generate Plots with Two Vertical Scales" describes an official recommended way to plot two functions with different vertical axis scales. The solution, in my opinion, is hideous:

TwoAxisPlot[{f_, g_}, {x_, x1_, x2_}] := 
 Module[{fgraph, ggraph, frange, grange, fticks, gticks},

{fgraph, ggraph} = MapIndexed[Plot[#, {x, x1, x2}, Axes -> True, PlotStyle -> ColorData[1][#2[[1]]]] &, {f, g}];
{frange, grange} = (PlotRange /. AbsoluteOptions[#, PlotRange])[[2]] & /@ {fgraph, ggraph};
fticks = N@FindDivisions[frange, 5]; 
gticks = Quiet@Transpose@{fticks, ToString[NumberForm[#, 2], StandardForm] & /@ Rescale[fticks, frange, grange]}; 
Show[fgraph, ggraph /. Graphics[graph_, s___] :> Graphics[
GeometricTransformation[graph, RescalingTransform[{{0, 1}, grange}, {{0, 1}, frange}]], s],
Axes -> False, Frame -> True, FrameStyle -> {ColorData[1] /@ {1, 2}, {Automatic, Automatic}},
FrameTicks -> {{fticks, gticks}, {Automatic, Automatic}}]]

I made it a bit more readable, with only one change in functionality (add the ability to specify the range of each plot manually) below:

TwoAxisPlot[{f_, g_}, {x_, x1_, x2_}, fRange_ : Automatic, gRange_ : Automatic] := 
 Module[{plotInput, fPlot, gPlot, gPlotScaled, fRangeUsed, gRangeUsed, fTicks, gTicks},

  plotInput = {{f, g}, ColorData[1][#] & /@ {1, 2}, {fRange, gRange}};
  {fPlot, gPlot} = MapThread[Plot[#1, {x, x1, x2}, Axes -> True, PlotStyle -> #2, PlotRange -> #3] &, plotInput];
  {fRangeUsed, gRangeUsed} = Last @@@ Values[AbsoluteOptions[{fPlot, gPlot}, PlotRange]];
  gPlotScaled = Graphics[GeometricTransformation[First[gPlot],
     RescalingTransform[{{0, 1}, gRangeUsed}, {{0, 1}, fRangeUsed}]], Last[gPlot]];
  fTicks = N[FindDivisions[fRangeUsed, 5]];
  gTicks = ToString[NumberForm[#, 2], StandardForm] & /@ Rescale[fTicks, fRangeUsed, gRangeUsed];
  Show[fPlot, gPlotScaled, Axes -> False, Frame -> True,
    FrameStyle -> {ColorData[1][#] & /@ {1, 2}, {Automatic, Automatic}},
    FrameTicks -> {{fTicks, Transpose[{fTicks, gTicks}]}, {Automatic, Automatic}}]]

I've seen questions about how this function works on other StackOverflow posts, with no answers except that it's per the official documentation. The part that was particularly confusing was:

Show[fgraph, ggraph /. Graphics[graph_, s___] :> Graphics[...

It turns out that Head[Plot[x,{x,0,1}] IS a Graphics[graph_, s___] object; the above Replace is just plain weird and definitely confusing to a newbie.

Is there any loss of generality with the readability changes I've made???

It seems like this is a feature that almost everybody would eventually need to use, even high-school level users, and it's very convoluted if you're not a Mathematica guru. Granted, you can "just use it", but it's not very general-case, and if you want to modify it, you have to understand it to some degree at least.

$\endgroup$
3

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.