# Is it possible to extend the ResourceFunction CombinePlots for more than two axes?

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


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


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


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


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


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


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]


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


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


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


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


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]


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

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

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


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:

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


• 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 Commented Apr 28, 2023 at 9:06
• @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. Commented Apr 28, 2023 at 11:55
• Sorry@Lukas Lang, I will accept kglr answer, it gives more freedom to control ticks length, label, and axes position. Commented May 1, 2023 at 11:28