29
$\begingroup$

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?

$\endgroup$
4
  • 9
    $\begingroup$ (+1) A very good example of what should be achievable with MMa in the easy! $\endgroup$ Jun 16, 2015 at 18:23
  • 6
    $\begingroup$ This takes an unnatural amount of work (which it shouldn't). Maybe these related posts help: FrameTicks in BarChart, Labeling a bar chart, changing how rotated labels are centered. $\endgroup$
    – Jens
    Jun 16, 2015 at 19:03
  • 5
    $\begingroup$ @Jens Especially, because one of the most useful functions for achieving this is broken since literally ever: AbsoluteOptions $\endgroup$
    – halirutan
    Jun 17, 2015 at 0:48
  • 2
    $\begingroup$ @halirutan Even more handy function for such kind of tasks is FullGraphics but it does not work either. $\endgroup$ Jun 21, 2015 at 2:56

4 Answers 4

34
$\begingroup$

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})
    ]
   ];
  1. plot can be any plot or chart.
  2. 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).
  3. The optional argument 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}}
 ]

chart

GapAxes[
 Plot[Tan[x], {x, -3, 3}], 
 {{-3, 3, TickDirection -> Out}, {-6, 6, TickDirection -> Out}}
 ]

plot


Notes:

  1. It remains to be seen how robust this GapAxes function will prove to be, but the basic method should be pretty universal.

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

chart2

$\endgroup$
0
10
$\begingroup$

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

enter image description here

a bit of caution, I'm not certain the axes are precisely aligned.

$\endgroup$
5
$\begingroup$

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

enter image description here

This post is potentially useful for aligning three Insets

$\endgroup$
6
  • 1
    $\begingroup$ try working with regular Plot for the axes then you can specify the PlotRange $\endgroup$
    – george2079
    Jun 16, 2015 at 19:37
  • $\begingroup$ @george2079 I can specify 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, 2015 at 19:41
  • $\begingroup$ yes you can. Actually the other issue I had in v9 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, 2015 at 19:52
  • $\begingroup$ why do you need an empty plot? $\endgroup$
    – BlacKow
    Jun 16, 2015 at 19:55
  • 1
    $\begingroup$ that's what your 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, 2015 at 20:01
3
$\begingroup$
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}] &;

Histogram

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

enter image description here

ListPlot

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

enter image description here

BarChart

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

enter image description here

Plot

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

enter image description here

Note: With some additional effort, some of the manual settings can be automated using Scaled and/or extracting plot range.

$\endgroup$

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.