Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a grid of plots with a single pair of FrameLabels?

What is the simplest way to create a row/column/grid of plots, with the whole grid having a single FrameLabel?

I need something similar to this:

p := ListPlot[RandomInteger[10, 5], Joined -> True, Axes -> False, 
  Frame -> True, PlotRange -> {0, 11}, 
  FrameLabel -> {"horizontal", None}, AspectRatio -> 1]

GraphicsRow[{Show[p, FrameLabel -> {"horizontal", "vertical"}], p, p}]

For a row format, it could have one or multiple horizontal labels, but only one vertical one.

Issues to consider:

  • Vertical scale must match for all plots, and must not be ruined by e.g. a too long label or automatic PlotRangePadding.
  • Good (and resize-tolerant!) control of inter-plot spacing is needed (after all, this is one of the motivations behind removing the redundant labels)
  • General space-efficiency of the arrangement. Maximum content, minimum (unnecessary) whitespace.

EDIT

I'm trying to be able to robustly create print ready figures, which involves a lot of resizing. (Because the exported PDFs will usually not have the same proportions as what I see in the notebook, and must have readable but not oversized fonts)

like image 923
Szabolcs Avatar asked Apr 28 '11 12:04

Szabolcs


3 Answers

You can use LevelScheme to achieve what you want. Here's an example:

<< "LevelScheme`"
Figure[{
  Multipanel[{{0, 1}, {0, 1}}, {1, 3},
   XFrameLabels -> textit["x"], BufferB -> 3,
   YFrameLabels -> textit["Sinc(x)"], BufferL -> 3,
   TickFontSize -> 9,
   XGapSizes -> {0.1, 0.1},
   PanelLetterCorner -> {1, 1}
   ],
  FigurePanel[{1, 1}, PlotRange -> {{-1.6, -0.6}, {-0.5, 1}}],
  RawGraphics[Plot[Sinc[20 x], {x, -1.6, -0.6}]],

  FigurePanel[{1, 2}, PlotRange -> {{-0.5, 0.5}, {-0.5, 1}}],
  RawGraphics[Plot[Sinc[20 x], {x, -0.5, 0.5}]],

  FigurePanel[{1, 3}, PlotRange -> {{0.6, 1.6}, {-0.5, 1}}],
  RawGraphics[Plot[Sinc[20 x], {x, 0.6, 1.6}]]
  },
 PlotRange -> {{-0.1, 1.02}, {-0.12, 1.095}}]

enter image description here

LevelScheme offers you tremendous flexibility in the arrangement of your plot.

  • Instead of naming giving the plot common labels, you can move the definition inside the FigurePanel[] and control the labels for each one individually.
  • You can set inter-plot spacings both in the X and Y directions and also change the sizes of each panel, for e.g., the left one can take up 2/3 of the space and the next two just 1/6 of the space each.
  • You can set individual plot ranges, change the frame tick labels for each, control which side of the panel (top/bottom/l/r) the labels should be marked, change panel numberings, etc.

The only drawback is that you might have to wrestle with it in some cases, but in general, I've found it a pleasure to use.

EDIT

Here's one similar to your example:

Figure[{
  Multipanel[{{0, 1}, {0, 1}}, {1, 3},
   YFrameLabels -> textit["Vertical"], BufferL -> 3,
   TickFontSize -> 9,
   XGapSizes -> {0.1, 0.1},
   PanelLetterCorner -> {1, 1}
   ],
  FigurePanel[{1, 1}, PlotRange -> {{1, 10}, {0, 10}}],
  RawGraphics[ListLinePlot[RandomInteger[10, 10]]],

  FigurePanel[{1, 2}, PlotRange -> {{1, 10}, {0, 10}},
   LabB -> textit["Horizontal"], BufferB -> 3],
  RawGraphics[ListLinePlot[RandomInteger[10, 10]]],

  FigurePanel[{1, 3}, PlotRange -> {{1, 10}, {0, 10}}],
  RawGraphics[ListLinePlot[RandomInteger[10, 10]]]
  },
 PlotRange -> {{-0.1, 1.02}, {-0.2, 1.095}}]

enter image description here

EDIT 2

To answer Mr. Wizard's comment, here's a blank template for a 2x3 grid

Figure[{Multipanel[{{0, 1}, {0, 1}}, {2, 3},
   XFrameTicks -> None,
   YFrameTicks -> None,
   XGapSizes -> {0.1, 0.1},
   YGapSizes -> {0.1}],
  FigurePanel[{1, 1}],
  FigurePanel[{1, 2}],
  FigurePanel[{1, 3}],
  FigurePanel[{2, 1}],
  FigurePanel[{2, 2}],
  FigurePanel[{2, 3}]
  }, PlotRange -> {{-0.01, 1.01}, {-0.01, 1.01}}]    

enter image description here

And here's one with extended panels

Figure[{Multipanel[{{0, 1}, {0, 1}}, {2, 3},
   XFrameTicks -> None,
   YFrameTicks -> None,
   XGapSizes -> {0.1, 0.1},
   YGapSizes -> {0.1}],
  FigurePanel[{1, 1}, PanelAdjustments -> {{0, 0}, {1.1, 0}}],
  FigurePanel[{1, 2}],
  FigurePanel[{1, 3}],
  FigurePanel[{2, 2}, PanelAdjustments -> {{0, 1.1}, {0, 0}}]
  }, PlotRange -> {{-0.01, 1.01}, {-0.01, 1.01}}]

enter image description here

like image 160
abcd Avatar answered Nov 20 '22 05:11

abcd


You already know how to handle multiple horizontal labels through ListPlot. You can get single labels by using Panel. For example...

p := ListPlot[RandomInteger[10, 5], Joined -> True, Axes -> False, 
Frame -> True, PlotRange -> {0, 11}, AspectRatio -> 1]

Panel[GraphicsRow[{p, p, p}], {"horizontal",Rotate["vertical", Pi/2]}, 
      {Bottom, Left}, Background -> White]

triptych

You can optionally include labels on Top and Right edges too.

like image 42
DavidC Avatar answered Nov 20 '22 06:11

DavidC


Here is one option I just put together. Its advantage is that it is simple.

I like the look of yoda's LevelScheme plots better, assuming those can be done for a grid as well.

p := ListPlot[RandomInteger[10, 5], Joined -> True, Axes -> False, 
  Frame -> True, PlotRange -> {0, 11}, AspectRatio -> 1]

gg = GraphicsGrid[{{p, p, p}, {p, p, p}, Graphics /@ Text /@ {"Left", "Center", "Right"}},
       Spacings -> 5, ItemAspectRatio -> {{1, 1, 0.15}}];

Labeled[gg, Rotate["vertical", Pi/2], Left]

enter image description here

like image 3
Mr.Wizard Avatar answered Nov 20 '22 06:11

Mr.Wizard