5
$\begingroup$

Assume there are three data and would like to plot as comment x-axis and three y-axis and present them precisely as the ResourceFunction["CombinePlots"] does for two data with two Y axes, can we extend that this function for more more than two Y axes?

dat1 = Table[{10.^i, Log[10.^i]}, {i, 1, 10}];
dat2 = Table[{10.^i, i Log[10.^i]}, {i, 1, 10}];
dat3 = Table[{10.^i, i Sin[i] Log[10.^i]}, {i, 1, 10}];   

For two data we can d that like

ResourceFunction["CombinePlots"][
 ListLogLinearPlot[dat1, PlotRange -> All, Frame -> True, 
  ImageSize -> 450, FrameLabel -> {"X", "Y1"}, FrameStyle -> Blue, 
  LabelStyle -> {Black, FontSize -> 16}, PlotStyle -> Blue, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}], 
 ListLogLinearPlot[dat2, Frame -> True, FrameLabel -> {"X", "Y2"}, 
  PlotMarkers -> {Automatic, Scaled[0.03]}, 
  FrameStyle -> Darker@Green, PlotStyle -> Darker@Green, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}], 
 "AxesSides" -> "TwoY"]     

enter image description here

now, how can I add another y axis for dat3? and why dont PlotMarkers appear on the green curve?

Update1

the two answers by @kglr and Lukas are great, I still have one point regarding control of the PlotRange for each data. using @Lukas approach it gives

ResourceFunction["CombinePlots"][
 ListLogLinearPlot[dat1, PlotRange -> {0, 30}, Frame -> True, 
  ImageSize -> 450, FrameLabel -> {"X", "Y1"}, FrameStyle -> Blue, 
  LabelStyle -> {Black, FontSize -> 16}, PlotStyle -> Blue, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}], 
 ListLogLinearPlot[dat2, PlotRange -> {0, 300}, Frame -> True, 
  FrameLabel -> {"X", "Y2"}, PlotMarkers -> {Automatic, Scaled[0.03]},
   FrameStyle -> Darker@Green, PlotStyle -> Darker@Green, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}], 
 ListLogLinearPlot[dat3, PlotRange -> {-200, 200}, Frame -> True, 
  FrameLabel -> {"X", "Y3"}, PlotMarkers -> {Automatic, Scaled[0.03]},
   FrameStyle -> Orange, PlotStyle -> Orange, Joined -> True, 
  PlotMarkers -> {Automatic, Scaled[0.03]}], 
 "AxesSides" -> {Left, Right, Right -> 2}]      

enter image description here

$\endgroup$

2 Answers 2

12
$\begingroup$

Update 3: A poor-mans-combinePlots adding sub-options that we wish MultiaxisArrangement could handle out-of-the box

ClearAll[wishTheseMultiaxisOptionsWorked]

wishTheseMultiaxisOptionsWorked[o : OptionsPattern[]] :=
 Module[{RL = RotateLeft,
   lbls = "$axisLabels" /. {o}, 
   pr = "$plotRanges" /. {o}, 
   lpos = "$labelPositions" /. {o},
   apos = "$axisPositions" /. {o}, 
   xtl = "$xtickLengths" /. {o},
   ytl = "$ytickLengths" /. {o}}, 
  If[lbls === "$axisLabels", Identity, 
    ReplaceAll[
     {{AxisObject[{d_, p_, r_}, r2_, opts___], {{dir___, _Line}}} :> 
        AxisObject[{d, 
          If[apos === "$axisPositions", p, Last[apos = RL[apos]]], r}, r2,
         BaseStyle -> dir,
         AxisLabel -> Placed[Last[lbls = RL[lbls]],
          If[ lpos === "$labelPositions", {1.05, {0, .5}}, 
            Last[lpos = RL[lpos]]]], 
         TickLengths -> If[ytl === "$ytickLengths", {.035, .015}, ytl],
         LabelStyle -> dir, opts],
      AxisObject[dr : {"Horizontal", ___}, x_, y___] :>
        AxisObject[dr, x,
         TickLengths -> 
           If[ xtl === "$xtickLengths", {.035, .015}, xtl], y]} ]]@*
   If[pr === "$plotRanges", Identity,
    ReplaceAll[
     {Inset[i_, p___] :>
       Inset[Show[i, PlotRange -> {First@PlotRange[i], Last[pr = RL[pr]]}], p],
     AxisObject[aa : {"Vertical", __}, b_, c___] :>
       AxisObject[aa, Thread[b[[All, 1]] -> Last[pr = RL[pr]]], c]} ]]]

Examples:

llp = ListLinePlot[{dat1, dat2, dat3}, PlotStyle -> plotStyles, 
  ScalingFunctions -> {"Log", "Linear"}, Mesh -> All, 
  LabelStyle -> 16, ImageSize -> Large, 
  MultiaxisArrangement -> {Left -> 1, Right -> {2, 3}}]

enter image description here

wishTheseMultiaxisOptionsWorked["$axisLabels" -> {"Y1", "Y2", "Y3"}] @ llp

enter image description here

wishTheseMultiaxisOptionsWorked[
  "$axisLabels" -> {"Y1", "Y2", "Y3"}, 
  "$plotRanges" -> {{0, 40}, {0, 300}, {-200, 200}}] @ llp

enter image description here

wishTheseMultiaxisOptionsWorked[
  "$labelPositions" -> {{0.5, {0.5, -2}}, {0.5, {0.5, 3}}, {0.5, {0.5, 3}}},
  "$axisLabels" -> {"Y1", "Y2", "Y3"}, 
  "$plotRanges" -> {{0, 40}, {0, 300}, {-200, 200}}, 
  "$axisPositions" -> {0, 1, Offset[80, 1]}] @ llp

enter image description here

Update 1: The option MultiaxisArrangement gives most of what we need. It does not work with ListLogLinearPlot so we use ListLinePlot and use ScalingFunctions to get the desired scaling:

llp = ListLinePlot[{dat1, dat2, dat3}, 
  PlotStyle -> {Blue, Darker@Green, Orange}, 
  ScalingFunctions -> {"Log", "Linear"}, 
  PlotMarkers -> {Automatic, Scaled[0.03]}, 
  MultiaxisArrangement -> {Right -> {2, 3}, Left -> {1}}, 
  LabelStyle -> 16, ImageSize -> Large]

enter image description here

We post-process to make the desired changes in styling and labeling:

labels = "Y" <> ToString[#] & /@ {1, 2, 3};

llp2 = llp /. 
  {{a_AxisObject, {{dir___, _Line}}} :> 
     Insert[a, {BaseStyle -> dir, 
       AxisLabel -> Placed[Last[labels = RotateLeft[labels]], {1.05, {0, .5}}], 
       TickLengths -> {.035, .015}, LabelStyle -> dir}, 3], 
    AxisObject[a : {"Horizontal", ___}, b_, c___] :> 
       AxisObject[a, b, TickLengths -> {.035, .015}, c]}

enter image description here

Update 2: Post-process to modify the plot ranges of individual plots:

pranges = {{0, 30}, {0, 300}, {-200, 200}};

llp2 /. {Inset[i_, p___] :> 
   Inset[Show[i, PlotRange -> {First@PlotRange[i], 
       Last[pranges = RotateLeft[pranges]]}], p], 
  AxisObject[a : {"Vertical", __}, b_, c___] :> 
   AxisObject[a, Thread[b[[All, 1]] -> Last[pranges = RotateLeft[pranges]]], c]}

enter image description here

Original answer:

Looks like ResourceFunction["CombinePlots"] does not support more than 2 axes in each direction. (As of this Q/A it DOES -- see this answer by Lukas Lang)

A work-around is to use AxisObject to create a custom axis object and place it as Epilog:

combinePlots = ResourceFunction["CombinePlots"];

options = {PlotRangePadding -> 0, PlotRange -> All, 
  Frame -> True, ImageSize -> 450, 
  LabelStyle -> {Black, FontSize -> 16}, PlotStyle -> Blue, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}}; 

{lp1, p2, lp3} = MapThread[ListLogLinearPlot[#, FrameLabel -> {"X", #3}, 
      FrameStyle -> #2, PlotStyle -> #2, options] &] @
  {{dat1, dat2, dat3}, {Blue, Darker@Green, Orange}, {"Y1", "Y2", "Y3"}};

First, we can nest combinePlot to get all three plots appropriately scaled in a single plot:

lp123 = combinePlots[lp1, 
   combinePlots[lp2, lp3, "AxesSides" -> "TwoY"], 
   "AxesSides" -> "TwoY"]

enter image description here

Next, we extract the vertical plot range of lp123 to be used in specifying the axis range in AxisObject:

vrange = Last @ PlotRange @ lp123;

hpos = 1.15;

y3Axis = AxisObject[{"Vertical", Scaled[hpos], vrange}, 
   Last @ PlotRange @ lp3, 
   TickPositions -> {6, 6}, TickLengths -> {0.75, .4}, 
   TickDirection -> Up, BaseStyle -> Orange, 
   LabelStyle -> Directive[16, Orange], 
   AxisLabel -> Placed["Y3", {.5, {0.5, 3.}}], RotateLabel -> 90 °];

Use y3Axis as Epilog in combinePlots:

combinePlots[lp1, combinePlots[lp2, lp3, "AxesSides" -> "TwoY"], 
 "AxesSides" -> "TwoY", 
 Epilog -> y3Axis,  
 ImagePadding -> {{Automatic, Scaled[.18]}, {Automatic, Automatic}}, 
 ImageSize -> 700, PlotRangePadding -> Scaled[.05],]

enter image description here

To verify that the new axis ticks are correctly positioned, reverse the positions of lp2 and lp3 in the inner combinePlot and make ticks longer in y3Axis:

combinePlots[lp1, combinePlots[lp3, lp2, "AxesSides" -> "TwoY"], 
 "AxesSides" -> "TwoY", PlotRangePadding -> Scaled[.05], 
 Epilog -> y3Axis /. {Orange -> Purple, 
   HoldPattern[TickLengths -> _] -> Rule[TickLengths, {5, 1}]},  
 ImagePadding -> {{Automatic, Scaled[.18]}, {Automatic, Automatic}}, 
 ImageSize -> 700]

enter image description here

$\endgroup$
4
  • $\begingroup$ would it be possible to control the plot range for each plot independently, kindly see my updat $\endgroup$
    – MMA13
    Commented Apr 28, 2023 at 9:22
  • $\begingroup$ @valarmorghulis, please see update 2. $\endgroup$
    – kglr
    Commented Apr 28, 2023 at 23:11
  • $\begingroup$ that is nice, thank you! why do ticks length on x axes are smaller compared to ones on y-axes? $\endgroup$
    – MMA13
    Commented Apr 28, 2023 at 23:16
  • 1
    $\begingroup$ that's because I did not touch the horizontal axes specs. Now added another layer of replacement to modify the horizontal axes. $\endgroup$
    – kglr
    Commented Apr 28, 2023 at 23:38
9
$\begingroup$

I just submitted an updated version of ResourceFunction["CombinePlots"] that supports more than two horizontal/vertical axes. It also fixes the issue with the squished plot markers.

Update: It has now been accepted, and is available simply as ResourceFunction["CombinePlots"]

Now, to your question:

dat1 = Table[{10.^i, Log[10.^i]}, {i, 1, 10}];
dat2 = Table[{10.^i, i Log[10.^i]}, {i, 1, 10}];
dat3 = Table[{10.^i, i Sin[i] Log[10.^i]}, {i, 1, 10}];   

ResourceFunction["CombinePlots"][
 ListLogLinearPlot[dat1, PlotRange -> All, Frame -> True, 
  ImageSize -> 450, FrameLabel -> {"X", "Y1"}, FrameStyle -> Blue, 
  LabelStyle -> {Black, FontSize -> 16}, PlotStyle -> Blue, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}],
 ListLogLinearPlot[dat2, Frame -> True, FrameLabel -> {"X", "Y2"}, 
  PlotMarkers -> {Automatic, Scaled[0.03]}, 
  FrameStyle -> Darker@Green, PlotStyle -> Darker@Green, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}],
 ListLogLinearPlot[dat3, Frame -> True, FrameLabel -> {"X", "Y3"}, 
  PlotMarkers -> {Automatic, Scaled[0.03]}, FrameStyle -> Orange, 
  PlotStyle -> Orange, Joined -> True, 
  PlotMarkers -> {Automatic, Scaled[0.03]}], "AxesSides" -> "MultiY"
 ]

enter image description here

Note the use of the new "AxesSides"->"MultiY". Other than that, I only added the third plot to be combined with the others. If you'd rather have the third axis on the right, use "AxesSides" -> {Left, Right, Right -> 2}, or "AxesSides"->{Left, {Right -> Automatic}} if you want all additional plots to get their own axis on the right:

enter image description here

Updated version

I have fixed the issue with the second axis on the right being shifted upwards, now it should work as expected:

ResourceFunction["CombinePlots"][
 ListLogLinearPlot[dat1, PlotRange -> {0, 30}, Frame -> True, 
  ImageSize -> 450, FrameLabel -> {"X", "Y1"}, FrameStyle -> Blue, 
  LabelStyle -> {Black, FontSize -> 16}, PlotStyle -> Blue, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}],
 ListLogLinearPlot[dat2, PlotRange -> {0, 300}, Frame -> True, 
  FrameLabel -> {"X", "Y2"}, PlotMarkers -> {Automatic, Scaled[0.03]},
   FrameStyle -> Darker@Green, PlotStyle -> Darker@Green, 
  Joined -> True, PlotMarkers -> {Automatic, Scaled[0.03]}],
 ListLogLinearPlot[dat3, PlotRange -> {-200, 200}, Frame -> True, 
  FrameLabel -> {"X", "Y3"}, PlotMarkers -> {Automatic, Scaled[0.03]},
   FrameStyle -> Orange, PlotStyle -> Orange, Joined -> True, 
  PlotMarkers -> {Automatic, Scaled[0.03]}], 
 "AxesSides" -> {Left, Right, Right -> 2}
 ]

enter image description here

$\endgroup$
3
  • $\begingroup$ that is great, however, adding PlotRange -> {0, 30} and PlotRange -> {0, 300} and PlotRange -> {-200, 200} for dat123 respectively, shift the third axes and frame ticks at the edge are cut. see update $\endgroup$
    – MMA13
    Commented Apr 28, 2023 at 9:06
  • 2
    $\begingroup$ @valarmorghulis Thanks for catching this! I have fixed the underlying issue & resubmitted the new version (see the new section at the end). In the meantime, you can force-update by running the ResourceRemove[...] line followed by the ResourceRegister[...] line from the answer. $\endgroup$
    – Lukas Lang
    Commented Apr 28, 2023 at 11:55
  • $\begingroup$ Sorry@Lukas Lang, I will accept kglr answer, it gives more freedom to control ticks length, label, and axes position. $\endgroup$
    – MMA13
    Commented May 1, 2023 at 11:28

Your Answer

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

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