14
$\begingroup$

I am making a bunch of two dimensional plots and I would like to have them arranged in a grid with no space in between them. That is, I want their frames touching each other.

My problem is twofold. First I don't know how to get the plots to have a consistent size. When I try to give the plots the same size via ImageSize, it applies the size to the overall image, including the tick marks and labels. But when I have two different plots, one with tick labels and one without, giving the same value to ImageSize doesn't give the result I'm looking for.

Secondly, I want to get rid of any white space around the images such that when I give Grid the option Spacings ->{0,0} there really is no visible space between the plots.

In the example code I use the CustomTicks package.

(*an example of what I might plot, simple sum of two dimensional \
Lorentzians*)
lzn[x_, w_, g_] := (x - w + I g)^-1;
exampledata2D = 
  Table[Re[lzn[w1, .2, .1] lzn[w2, .3, .2] + 
     lzn[w1, .4, .1] lzn[w2, .8, .1] + 
     lzn[w1, .8, .2] lzn[w2, .4, .2]], {w1, 0, 1, .01}, {w2, 0, 
    1, .01}];
<< "CustomTicks`";  
standardticks = LinTicks[0, 1, .2, 4];
(*I don't want the last tick label for one plot overlapping with the \
first tick label of the plot right next to it.*)
ticks2 = LinTicks[0, 1, .2, 4, ShowLast -> False];
(*Custom plotting function so I don't have to reprint the same \
options over and over *)
plottingfunction[list_, plotopts : OptionsPattern[]] := 
  ListContourPlot[list, 
   Evaluate[FilterRules[{plotopts}, Options[ListContourPlot]]], 
   Contours -> 30, PlotRange -> All, DataRange -> {{0, 1}, {0, 1}}, 
   BaseStyle -> 18, 
   ColorFunction -> (Blend[{Red, Orange, Yellow, Green, Blue, 
        Darker[Blue]}, #] &)];

bottomleft = 
  plottingfunction[exampledata2D, 
   FrameTicks -> {{ticks2, StripTickLabels[standardticks]}, {ticks2, 
      StripTickLabels[standardticks]}}];
bottomright = 
  plottingfunction[exampledata2D, 
   FrameTicks -> {{StripTickLabels[standardticks], 
      StripTickLabels[standardticks]}, {standardticks, 
      StripTickLabels[standardticks]}}];
topleft = 
  plottingfunction[exampledata2D, 
   FrameTicks -> {{ticks2, 
      StripTickLabels[standardticks]}, {StripTickLabels[
       standardticks], StripTickLabels[standardticks]}}];
topright = 
  plottingfunction[exampledata2D, 
   FrameTicks -> {{StripTickLabels[standardticks], 
      StripTickLabels[standardticks]}, {StripTickLabels[
       standardticks], StripTickLabels[standardticks]}}];

Grid[{{topleft, topright}, {bottomleft, bottomright}}, 
 BaseStyle -> ImageSizeMultipliers -> 1, Spacings -> {0, 0}]

enter image description here

$\endgroup$
6
  • $\begingroup$ I realize this may be a duplicate, but I didn't find the answer when I searched around. $\endgroup$
    – Jason B.
    Nov 20, 2013 at 19:25
  • 1
    $\begingroup$ You may be interested in grid full of plots. Fix ImageSize, fix AspectRation, focus on FrameTicks, use ImagePadding for plot and Spacing for Grid. $\endgroup$
    – Kuba
    Nov 20, 2013 at 19:30
  • $\begingroup$ A good solution might be to use the LevelScheme package which can handle these things. This answer mentions it. $\endgroup$
    – Szabolcs
    Nov 20, 2013 at 19:34
  • $\begingroup$ More resources here and here. $\endgroup$
    – Szabolcs
    Nov 20, 2013 at 19:50
  • $\begingroup$ @Szabolcs, if you added the disclaimer at the top, could you change it to point to grid full of plots instead of Aligning plot axes in a graphics object as that is the post I used to solve this. Thanks. $\endgroup$
    – Jason B.
    Nov 20, 2013 at 20:33

2 Answers 2

17
$\begingroup$

Another way to do this is to use the SciDraw package (LevelScheme successor). (Note: I'm still learning to use this package and I may not be doing things in the most efficient way. Also, the package is still in beta as of November 2013.)

This is the plot we're going to have in the grid:

plot = plottingfunction[exampledata2D];

First, load the package:

<<SciDraw`

A very simple way to construct the grid, exploiting the fact that all nine subplots are identical, is the following:

Figure[
 Multipanel[
  {
   FigurePanel[{FigGraphics[plot]}, All, (* "All" means evaluate this for all subplots *)
     PanelLetterTextColor -> White];
  },

  Dimensions -> {3, 3}  (* a 3 by 3 grid *)
 ],

 CanvasSize -> 8 {1, 1} (* the canvas aspect ratio sets the subplot aspect ratio too *)
]

Of course in practical situations the subplots are all different. Here's a more realistic way to do it which is convenient if the subplots are different but have the same plot range:

DefineStyle["ContourArray",
 {
  FigurePanel -> {
    XPlotRange -> {0, 1}, (* these need to be set if different from {0,1} *)
    YPlotRange -> {0, 1},
    YTickLabelRange -> {0.2, 1},
    XTickLabelRange -> {0.2, 1},
    PanelLetterTextColor -> White }
 }
]

Figure[
 Multipanel[
  {
   FigurePanel[{FigGraphics[plot]}, {1, 1}];
   FigurePanel[{FigGraphics[plot]}, {1, 2}];
   FigurePanel[{FigGraphics[plot]}, {1, 3}];
   FigurePanel[{FigGraphics[plot]}, {2, 1}];
   FigurePanel[{FigGraphics[plot]}, {2, 2}];
   FigurePanel[{FigGraphics[plot]}, {2, 3}];
   FigurePanel[{FigGraphics[plot]}, {3, 1}, XTickLabelRange -> All];
   FigurePanel[{FigGraphics[plot]}, {3, 2}];
   FigurePanel[{FigGraphics[plot]}, {3, 3}];       
  },

  Dimensions -> {3, 3}
 ],

 Style -> "ContourArray",
 CanvasSize -> 8 {1, 1}
]

enter image description here

Note how the tick label overlap was solved: the default tick range was set to $0.2 - 1.0$ and it was overridden on the bottom left panel only.

To disable the automatic panel lettering, use PanelLetter -> None in the style.

$\endgroup$
5
  • 3
    $\begingroup$ Wow! SciDraw is the answer to my Mathematica plotting prayers ... I want to go have a cry I am so happy. $\endgroup$
    – Gabriel
    Dec 9, 2013 at 0:42
  • $\begingroup$ I notice that the documentation and your example makes the arguments to *Panel's as a list of a single element, using ; to separate the different plotting directives. I find you can just change this to a list (which seems more natural to me). Is there any reason for the ; style or is it just preference? Things like this always suggest my lack of deep MMA knowledge... $\endgroup$
    – Gabriel
    Dec 9, 2013 at 2:24
  • 1
    $\begingroup$ To answer my own question ... the use of ; is stylistic and is explained in section 10.1 of the SciDraw documentation. $\endgroup$
    – Gabriel
    Dec 9, 2013 at 3:32
  • $\begingroup$ Great answer, great package, just one question I can't figure out: How can I set AspectRatio to something other than 1 while using Multipanel or FigurePanel? SciDraw seems to override it as an argument given to the 'plottingfunction' (i.e. ListPlot), and I can't find any helpful option in the SciDraw documentation. For reference, I'm using v.0.0.7 of SciDraw. $\endgroup$
    – Max
    Feb 26, 2017 at 0:37
  • 1
    $\begingroup$ @Max You can adjust the CanvasSize option at the very end. $\endgroup$
    – Szabolcs
    Feb 26, 2017 at 7:37
8
$\begingroup$

Thanks to Kuba and Szabolcs for pointing out many related posts. I recognize that LevelScheme is probably the best way to go here, but at the moment I don't have the time to learn everything I need in order to use it.

I am going to use Jens's solution from this page, but I have to remove the certain tick labels so that they don't overlap each other.

(*an example of what I might plot, simple sum of two dimensional \
Lorentzians*)
lzn[x_, w_, g_] := (x - w + I g)^-1;
exampledata2D = 
  Table[Re[lzn[w1, .2, .1] lzn[w2, .3, .2] + 
     lzn[w1, .4, .1] lzn[w2, .8, .1] + 
     lzn[w1, .8, .2] lzn[w2, .4, .2]], {w1, 0, 1, .01}, {w2, 0, 
    1, .01}];
<< "CustomTicks`";  
tickbottom = LinTicks[-1, 1, .5, 5, ShowLast -> False];
ticktop = LinTicks[-1, 1, .5, 5, ShowFirst -> False];
tickmiddle = 
  LinTicks[-1, 1, .5, 5, ShowFirst -> False, ShowLast -> False];
standardticks = LinTicks[-1, 1, .5, 5];
plottingfunction[list_, horizontalposition_, verticalposition_, 
   plotopts : OptionsPattern[]] := 
  ListContourPlot[list, 
   Evaluate[FilterRules[{plotopts}, Options[ListContourPlot]]], 
   Contours -> 30, DataRange -> {{-1, 1}, {-1, 1}}, BaseStyle -> 18, 
   ColorFunction -> (Blend[{Red, Orange, Yellow, Green, Blue, 
        Darker[Blue]}, #] &), PlotRangePadding -> None, 
   PlotRange -> All, 
   FrameTicks -> {{Which[verticalposition == "Top", ticktop, 
       verticalposition == "Middle", tickmiddle, 
       verticalposition == "Bottom", tickbottom], 
      StripTickLabels[standardticks]}, {If[
       verticalposition == "Bottom", 
       Which[horizontalposition == "Right", ticktop, 
        horizontalposition == "Middle", tickmiddle, 
        horizontalposition == "Left", tickbottom], 
       StripTickLabels[standardticks]], 
      StripTickLabels[standardticks]}}];

Options[plotGrid] = {ImagePadding -> 40};
plotGrid[l_List, w_, h_, opts : OptionsPattern[]] := 
 Module[{nx, ny, sidePadding = OptionValue[plotGrid, ImagePadding], 
   topPadding = 0, widths, heights, dimensions, positions, 
   frameOptions = 
    FilterRules[{opts}, 
     FilterRules[Options[Graphics], 
      Except[{ImagePadding, Frame, FrameTicks}]]]}, {ny, nx} = 
   Dimensions[l];
  widths = (w - 2 sidePadding)/nx Table[1, {nx}];
  widths[[1]] = widths[[1]] + sidePadding;
  widths[[-1]] = widths[[-1]] + sidePadding;
  heights = (h - 2 sidePadding)/ny Table[1, {ny}];
  heights[[1]] = heights[[1]] + sidePadding;
  heights[[-1]] = heights[[-1]] + sidePadding;
  positions = 
   Transpose@
    Partition[
     Tuples[Prepend[Accumulate[Most[#]], 0] & /@ {widths, heights}], 
     ny];
  Graphics[
   Table[Inset[
     Show[l[[ny - j + 1, i]], 
      ImagePadding -> {{If[i == 1, sidePadding, 0], 
         If[i == nx, sidePadding, 0]}, {If[j == 1, sidePadding, 0], 
         If[j == ny, sidePadding, topPadding]}}, AspectRatio -> Full],
      positions[[j, i]], {Left, Bottom}, {widths[[i]], 
      heights[[j]]}], {i, 1, nx}, {j, 1, ny}], 
   PlotRange -> {{0, w}, {0, h}}, ImageSize -> {w, h}, 
   Evaluate@Apply[Sequence, frameOptions]]]

plotGrid[Table[
  plottingfunction[exampledata2D, h, 
   v], {v, {"Top", "Middle", "Bottom"}}, {h, {"Left", "Middle", 
    "Right"}}], 1000, 1000]

enter image description here

I'm not 100% happy with the way it looks, with the missing tick labels, but it is better than having the labels overlap or get cut off.

$\endgroup$
5
  • $\begingroup$ I use LevelScheme a lot and I don't suggest using it for this. Your solution is far better/simpler, IMO. LevelScheme works great for line plots, but I've found using 2D plots (images/ArrayPlots/ContourPlots, etc.) to be a royal pain in the ass. I'm not sure what you mean by "missing tick labels" — they all seem to be there. Am I missing something? $\endgroup$
    – rm -rf
    Nov 22, 2013 at 14:21
  • 2
    $\begingroup$ @rm-rf, in order that the 1.0 label from one plot not overlap with the -1.0 label from the next, I've omitted them both for the points where the plots meet. I don't think that's the most aesthetically pleasing solution. I'd rather omit one label or the other, but not both. The problem with that is that both labels hang off the side just a bit, so when I trim the plots so they don't have any padding, it cuts off a portion of the label I'm keeping. I think what I'll do now is just add the 1.0 in that spot in Inkscape, since I'm going to be adding plot labels in Inkscape anyways. $\endgroup$
    – Jason B.
    Nov 22, 2013 at 17:15
  • $\begingroup$ That is a problem I've often faced (and I like making plots like these). My approaches to fix this has been to either label the x/y ticks in detail on the lower left plot and nowhere else (but tick marks still present) or to have a gap between the plots to accommodate the two extra ticks (I usually favour the second approach). Having only one of them would be a tad confusing, especially if the plots are arranged without any gap. $\endgroup$
    – rm -rf
    Nov 22, 2013 at 18:58
  • $\begingroup$ This question is closed, but I really wanted to add another solution using the SciDraw package, that I am learning to use now. I added the solution to your answer, just edit it to your liking ... @rm-rf with this package it turned out to be relatively convenient. $\endgroup$
    – Szabolcs
    Nov 27, 2013 at 17:28
  • $\begingroup$ @Szabolcs The question is open now. Feel free to post an answer $\endgroup$
    – rm -rf
    Nov 27, 2013 at 17:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.