See for example this picture produced with R:
You can see there is a small white space between the x-axis and the y-axis, so that the axes do not cross. How can I do this with Mathematica, for a ListPlot
or a Histogram
?
Mathematica Stack Exchange is a question and answer site for users of Wolfram Mathematica. It only takes a minute to sign up.
Sign up to join this communitySee for example this picture produced with R:
You can see there is a small white space between the x-axis and the y-axis, so that the axes do not cross. How can I do this with Mathematica, for a ListPlot
or a Histogram
?
This can be done more-or-less easily with a combination of options for AxesOrigin
, PlotRange
, and PlotRangePadding
and the CustomTicks
package (for easy outward-facing ticks).
Needs["CustomTicks`"];
GapAxes[plot_Graphics, ticks : {{x__}, {y__}}, scalefactor_: Automatic] := With[
{prange = ticks[[All, 1 ;; 2]],
s = Flatten@{scalefactor /. Automatic -> 0.02 {1, 1/(AspectRatio /. Options[plot])}}},
Show[plot,
Ticks -> {LinTicks[x], LinTicks[y]},
PlotRange -> (prange + Subtract @@@ prange {{First@s, 0}, {Last@s, 0}}),
PlotRangePadding -> (Subtract @@@ prange {{First@s, 0}, {Last@s, 0}}),
AxesOrigin -> (prange[[All, 1]] + Subtract @@@ prange {First@s, Last@s})
]
];
plot
can be any plot or chart. ticks
gives the arguments of the LinTicks
functions which specify the axes ticks. x
and y
must each contain a range specification (which also doubles as the PlotRange
specifiation) as the first two items, but they may also include as additional items any of the other arguments that may be passed to LinTicks
(TickDirection -> Out
, perhaps). scalefactor
specifies how far to separate the axes from the plot as a fraction of the total image dimensions. If scalefactor
is not specified, the axes are separated by 2% of the total width.Examples
data = RandomVariate[HalfNormalDistribution[1/150], 500];
GapAxes[
Histogram[data, {100}],
{{0, 700, TickDirection -> Out}, {0, 200, TickDirection -> Out}}
]
GapAxes[
Plot[Tan[x], {x, -3, 3}],
{{-3, 3, TickDirection -> Out}, {-6, 6, TickDirection -> Out}}
]
Notes:
It remains to be seen how robust this GapAxes
function will prove to be, but the basic method should be pretty universal.
To see the whole plot when the axes are short, additional ImagePadding
may be needed.
GapAxes[
Histogram[data, {100}, ImagePadding -> {{Automatic, 50}, {Automatic, Automatic}}],
{{0, 600, TickDirection -> Out}, {0, 200, TickDirection -> Out}}
]
This is tedious.. manually drawing the axes.
GraphicsRow[{Histogram[data],
Show[{Histogram[data , PlotRangePadding -> Scaled[.2],
Axes -> False,
PlotRange -> {{-3, 3}, {0, 100}}],
Graphics[{Line[{Scaled[{.2, .15}], Scaled[{.8, .15}]}],
Line[Scaled /@ {{#, .15}, {#, .1}}] & /@ Range[.2, .8, .1],
Text[#, Scaled[{.6 (# + 3)/6 + .2, .04}], {0, 0}] & /@
Range[-3, 3, 1],
Text[Rotate[ #, Pi/2],
Scaled[{.04, (#/100) .6 + .2 }], {0, 0}] & /@
Range[0, 100, 25],
Line[Scaled /@ {{.06, #}, {.1, #}}] & /@ Range[.2, .8, .1],
Line[Scaled /@ {{.1, .2}, {.1, .8}}]}]}]}]
a bit of caution, I'm not certain the axes are precisely aligned.
Not a real answer. But you can try to put your plot in Inset[]
, then add another Inset[]
for x-axis and yet another Inset[]
for y-axis
and then stitch all scales together…
Something like this (nothing is stitched)
Graphics[{Transparent, Rectangle[],
Inset[ListPlot[{1, 2, 3, 4, 0}, Axes -> False, Joined -> True,
InterpolationOrder -> 0, Filling -> Bottom], {0, 0}, {0.1, -0.1},
1], Inset[
ListPlot[{}, AxesStyle -> Red, Axes -> {False, True}], {0,
0.05}, {0, 0}, 1],
Inset[ListPlot[{}, AxesStyle -> Blue, Axes -> {True, False}], {0.05,
0}, {0, 0}, 1]}]
This post is potentially useful for aligning three Inset
s
Plot
for the axes then you can specify the PlotRange
$\endgroup$
– george2079
Jun 16 '15 at 19:37
PlotRange
for ListPlot
as well… like you said it's just so tedious! I actually prefer your solution, it's straightforward and quite flexible. I thought about it but got scared to draw ticks myself...
$\endgroup$
– BlacKow
Jun 16 '15 at 19:41
ListPlot
does not like an empty list. You can give it an out of range point though to get an empty plot.
$\endgroup$
– george2079
Jun 16 '15 at 19:52
ListPlot[{}, ..
does. Just an axis with no data. In v9 this throws an error "ListPlot called with 0 arguments" . This tricks it into working : ListPlot[{""}, ... ]
$\endgroup$
– george2079
Jun 16 '15 at 20:01
ClearAll[ticksF, axesF, labelF]
ticksF[tSide_: Left, tr_: 1, tl_: (.01), s_: {Thickness[.001]}][{minmax__}, nd_:{6, 6}] :=
Module[{tf = tSide /. {Automatic | Left -> Identity, Right -> ({-1, 1} # &),
Bottom -> ((Reverse@#) &)},
d = {#, Complement[Join @@ #2, #]} & @@ FindDivisions[{minmax}, nd, Method -> {}],
trns = tSide /. {Left -> {-tr, 0}, Right -> {tr, 0}, Bottom -> {0, -tr} }, tcks},
tcks = Join[Table[{i, i, tl, s}, {i, d[[1]]}], Table[{i, "", tl/2, s}, {i, d[[2]]}]];
Translate[{s, Line@Thread[tf @
{0, Through @ {Min, Max} @ #[[All, 1]]}], {Line[{tf @ {-#3, #}, tf @ {0, #}}],
Text[#2, tf[{1.2 tSide /. {(Left | Bottom) -> (-1) , (Top | Right) -> 1}, 1} {#3, #}],
If[tSide === Bottom, {Center, tSide /. {Bottom -> Top, Top -> Bottom}},
{tSide /. {Left -> Right, Right -> Left}, Center}]]} & @@@ #} &@tcks, trns]]
axesF[ tr_: {1, 1}, tl_: (0.01), ar_: 1/GoldenRatio][
{rng1 : {_, __}, nd1_: {6, 6}}, {rng2 : {_, __}, nd2_: {6, 6}}] :=
Module[{sc = ar (Subtract @@ rng1[[{2, 1}]])/ (Subtract @@ rng2[[{2, 1}]])},
{ticksF[Bottom, tr[[1]], tl][rng1, nd1], ticksF[Left, tr [[2]] sc, tl sc][rng2, nd2]}]
labelF = Labeled[#, {Rotate[#2, 90 Degree], #3}, {Left, Bottom}] &;
SeedRandom[1]
data = RandomVariate[HalfNormalDistribution[1/150], 500];
hst = Histogram[data, Axes -> False,
ImagePadding -> {Scaled /@ {.04, .05}, Scaled /@ {.04, .025}},
ImageSize -> 600, PlotRangeClipping -> False,
Epilog -> (axesF[{5, 5}, 5][{{0, 700}}, {{0, 120}}]),
BaseStyle -> {FontSize -> 14}];
labelF[hst, Style["labely", 20, "Panel"], Style["labelx", 20, "Panel"]]
lp = ListPlot[data, Axes -> False,
ImagePadding -> {Scaled /@ {.04, .05}, Scaled /@ {.04, .025}},
ImageSize -> 600, PlotRangeClipping -> False,
Epilog -> (axesF[{20, 20}, 20][{{0, Length@data}}, {{0, 1.1 Max[data]}}]),
BaseStyle -> {FontSize -> 14}];
labelF[lp, Style["labely", 20, "Panel"], Style["labelx", 20, "Panel"]]
bc = BarChart[d2 = HistogramList[data][[2]], Axes -> False,
AxesOrigin -> {0, 0},
ImagePadding -> {Scaled /@ {.025, .025}, Scaled /@ {.05, .025}},
ImageSize -> 600, AspectRatio -> ar, PlotRangeClipping -> False,
Epilog -> (axesF[{5, 0}, 5][{{1, 1 + Length@d2}, {Length@d2, 1}}, {{0, 120}}]),
BaseStyle -> {FontSize -> 14}];
labelF[bc, Style["labely", 20, "Panel"], Style["labelx", 20, "Panel"]]
plot = Plot[{Sin[x], Cos[x]}, {x, -2 Pi, 2 Pi}, Axes -> False,
ImagePadding -> {Scaled /@ {.05, .05}, Scaled /@ {.065, .05}},
ImageSize -> 600, PlotRange -> {{-2 Pi, 2 Pi}, {-1, 1}},
PlotRangeClipping -> False,
Epilog -> (axesF[{1.1, 1.7}, .1][{{-2 Pi, 2 Pi, Pi/2}, {8, 2}}, {{-1, 1}}]),
BaseStyle -> {FontSize -> 14}]
labelF[plot, Style["labely", 20, "Panel"], Style["labelx", 20, "Panel"]]
Note: With some additional effort, some of the manual settings can be automated using Scaled
and/or extracting plot range.
FullGraphics
but it does not work either. $\endgroup$ – Alexey Popkov Jun 21 '15 at 2:56