3
$\begingroup$

I want to do something similar to 1 Plot, 2 Scale/Axis but for the X-axis.

The aim is to have physical units below, but array indices at the top for easy access to the discrete data range.

So far I tried:

XY = Table[{i, Sin[i]}, {i, -10, 10, 0.5}];

DoubleXAxisPlotExample[XY : {{_?NumberQ, _?NumberQ} ..}] :=
  Overlay[{
    ListLinePlot[XY,
     ImagePadding -> 25,
     Frame -> True,
     FrameTicks -> {{All, None}, {All, None}},
     PlotRange -> All],
    ListLinePlot[Transpose[{Range[Length[XY]], Part[XY, All, 2]}],
     ImagePadding -> 25,
     Frame -> True,
     FrameTicks -> {{All, None}, {None, All}},
     PlotRange -> All]
    }];

DoubleXAxisPlotExample@XY returns:

enter image description here

However, with:

XY = Table[{i, 100000*Sin[i]}, {i, -10, 10, 0.5}];

DoubleXAxisPlotExample@XY returns:

enter image description here

-> the left ticks have been truncated

I understand that ImagePadding -> 25 is there to let enough arbitrary space on the left, however in my code this is a fixed absolute value. To solve the problem, I think that this value should be computed according to the actual plot Y-range, but I do not know how to do that.

Any idea?

$\endgroup$
4
$\begingroup$

You can use my function GraphicsInformation to obtain this information (I think GraphicsInformation is more reliable than Image + BorderDimensions). Install the paclet/function:

PacletInstall[
    "GraphicsInformation",
    "Site"->"http://raw.githubusercontent.com/carlwoll/GraphicsInformation/master"
];

Then, load it:

<<GraphicsInformation`

For your example:

plots = {
    ListLinePlot[
        XY,
        Frame -> True,
        FrameTicks -> {{All, None}, {All, None}},
        PlotRange -> All
    ],
    ListLinePlot[Transpose[{Range[Length[XY]], Part[XY, All, 2]}],
        Frame -> True,
        FrameTicks -> {{All, None}, {None, All}},
        PlotRange -> All
    ]
};
padding = "ImagePadding" /. GraphicsInformation[plots]

{{{44., 1.5}, {17., 0.5}}, {{44., 1.5}, {1.5, 16.}}}

This gives the actual ImagePadding used in the two plots. You can use MapThread to come up with the total ImagePadding that is needed:

MapThread[Max, padding, 2]

{{44., 1.5}, {17., 16.}}

I will leave it to you to include GraphicsInformation in your DoubleXAxisPlotExample function.

$\endgroup$
  • $\begingroup$ Thanks for this! Happy to see that I was not the only one to have problems with image padding! :) $\endgroup$ – Picaud Vincent Dec 14 '17 at 17:44
1
$\begingroup$

One can automate the image padding with a hack described here and built upon here.

Define two padding functions

padding[g_Graphics] := With[{im = Image[Show[g, LabelStyle -> White, Background -> White]]}, BorderDimensions[im]]
maxPadding[graphicsSequence__Graphics] := 1 + {Max /@ Transpose@(First /@ #), Max /@ Transpose@(Last /@ #)} &@(padding /@ List@graphicsSequence)

Then,

DoubleXAxisPlotExample[XY : {{_?NumberQ, _?NumberQ} ..}] := Module[{plt1, plt2, pad},
  plt1 = ListLinePlot[XY,
    PlotRange -> All,
    Frame -> True, FrameTicks -> {{All, None}, {All, None}}];
  plt2 = ListLinePlot[Transpose[{Range[Length[XY]], Part[XY, All, 2]}],
    PlotRange -> All,
    Frame -> True, FrameTicks -> {{All, None}, {None, All}}];
  pad = maxPadding[plt1, plt2];
  plt = Overlay[{
     Show[plt1, ImagePadding -> pad],
     Show[plt2, ImagePadding -> pad]
     }]
 ]

Then:

XY = Table[{i, 100000*Sin[i]}, {i, -10, 10, 0.5}];
DoubleXAxisPlotExample@XY

enter image description here

$\endgroup$
  • $\begingroup$ Thanks, for the hack (I was not aware of these links, sorry for the duplicate!). $\endgroup$ – Picaud Vincent Dec 14 '17 at 17:24

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.