StackedGraphicsArray

by Markus Lischka

The StackedGraphicsArray package provides an extension to GraphicsArray. It generates an array of framed plots with "shared" axes and no spacing between the framed plots. Ticks and labels are preserved from the individual plots where possible.

Reference

Title

StackedGraphicsArray.m — Graphics Array with "Shared" Axes

Author

Markus Lischka (mlischka@physik.tu-muenchen.de)

Summary

The StackedGraphicsArray package provides an extension to GraphicsArray. It generates an array of framed plots with "shared" axes and no spacing between the framed plots. Ticks and labels are preserved from the individual plots where possible.

Copyright

© Copyright 2000-2004, Markus Lischka.

Permission is granted to distribute this file for any purpose except for inclusion in commercial software or program collections. This copyright notice must remain intact.

Notebook Version

2.1

Mathematica Version

4.0, 5.0

History

Version 1.0: Initial version (2000-07-21).

Version 1.1: Added handling of None symbols representing empty plots (2000-08-18).

Version 1.2: Epilog options of single plots are now preserved (2001-02-15).

Version 1.3: Fixed bug when using StackedLabel option: Vector layout was ignored (2002-07-22).

Version 1.4: RotateLabel option is no longer ignored (2002-07-24).

Version 1.5: Initial submission to MathSource (2003-03-24).

Version 1.6: Replaced FullOptions by AbsoluteOptions (supersedes FullOptions starting with Mathematica 4.0). Fixed two bugs reported by Derrick Bass: plots generated with PlotRange -> All and plots having tick marks outside the plot range are now handled correctly; see filterTicksInPlotRange (2003-04-27)

Version 2.0: Major rewrite to make the package compatible with Mathematica 5.0: the plots are now arranged directly using Rectangle instead of using GraphicsArray and the tick labels are placed in one single step after composition using PlotRange -> All to avoid any clipping. For backwards compatibility, the option CompositionMode -> 1 was added. Furthermore, plots with different aspect ratios are now set to one common aspect ratio to avoid any misalignments (2003-09-24)

Version 2.1: Fixed a bug reported by Dale Horton (Omega Consulting) in addFrameLabelsScaled and addFrameLabels: Plots with single frame labels set to None (such as FrameLabel -> {x, y, z, None}) are now plotted correctly. In previous versions, a text label "{}" was erroneously added to the plot (2004-10-11)

Keywords

GraphicsArray, multiple plots, stacked plots

Source

Michele Cappellari, Graphics`MongoArray` package (MathSource #0208-538, http://library.wolfram.com/infocenter/MathSource/717)
Roman E. Maeder. Programming in Mathematica, 3rd ed. Addison-Wesley, 1996.

Warnings

Tick marks generated automatically might need to be adjusted so that they do not overlap. Resizing the final graphics often helps as the font size is fixed.

Dependent on the setting of CompositionMode, StackedGraphicsArray either returns a Graphics object (CompositionMode -> 2) or a GraphicsArray object (CompositionMode -> 1, deprecated). Be aware that graphics created with CompositionMode -> 1 are not properly rendered in Mathematica 5.0.

Plots need to be plotted with Frame->True as StackedGraphicsArray uses FrameTicks to determine the labels of the combined plot.

Note: all cells marked as "InitializationCell" will be written to the Auto-Save package. This package can then be read in programs that use it with Needs["Template`"]. Cells not intended to belong to the package should not have this property.

Limitations

The labels of the combined graphics are plotted via the Epilog option. Otherwise, a combination with zero spacing would not be possible. 

When using CompositionMode -> 1, i.e., the technique of version 1.x of this package, this implies that
(1) PlotRegion has to be extended (by default to {{0.15,0.85}, {0.15,0.85}}) to avoid cutting off the labels and
(2) exporting EPS will not work correctly due to inconsistent EPS rendering in Mathematica (at least in versions up to 4.x) as the labels will be clipped. Removing all lines that contain the single PostScript command "clip", e.g. by "sed -e 's/^clip$//' plot.eps > plot2.eps", solves this problem. Using the default CompositionMode -> 2 avoids these problems.

The front end of Mathematica 5.0 correctly clips any displayed graphics consistently with its EPS export feature. For Mathematica 5.0, CompositionMode -> 1 thus does not work for displaying graphics in Mathematica's front end.

Discussion

The idea of placing the labels via the Epilog option is taken from Graphics`MongoArray`. Look at that package for a different implementation of a stacked graphics array.

When combining multiple graphics into a graphics array, tick labels of the individual plots might overlap. For easy manipulation of tick labels see Elizabeth Praton's package Graphics`TickControl` (http://library.wolfram.com/infocenter/MathSource/5217). The accompanying guide includes an introduction on how to use both packages together.

Requirements

Utilities`FilterOptions`

Interface

This part declares the publicly visible functions, options, and values.

Set up the package context, including public imports

BeginPackage["Graphics`StackedGraphicsArray`", "Utilities`FilterOptions`"]

Usage messages for the exported functions and the context itself

StackedGraphicsArray::usage = "StackedGraphicsArray[{g1, g2, ... }] represents a row of graphics objects. StackdGraphicsArray[{{g11, g12, ... }, ... }] represents a two-dimensional array of graphics objects. The objects are combined with zero spacing between the graphics frames such that they \"share\" their axes, and interior labels are removed. The placement of labels can be controlled by the option StackedLabel.";

StackedLabel::usage = "StackedLabel is an option for StackedGraphicsArray that specifies which single plot labels should be removed. The default value Automatic removes all interior labels of the graphics array. Alternatively, it can be a list of the same form as the Graphics array to be processed. Each entry has to be a four-element list that specifies which labels should be suppressed (None) and which not (Automatic).";

CompositionMode::usage = "CompositionMode is an option for StackedGraphicsArray that specifies which composition technique to use for the graphics array. Valid choices for CompositionMode are 1 and 2. The default and recommended value is CompositionMode -> 2. The setting CompositionMode -> 1 uses the technique of version 1.x of the StackedGraphicsArray package. It is provided for backwards compatibility and might be removed in future versions as it is no longer compatible with Mathematica 5.0. Its use is strongly discouraged.";

Error messages for the exported objects

StackedGraphicsArray::labels = "Dimensions of GraphicsArray and StackedLabel are incompatible.";

StackedGraphicsArray::arrsp = "Value of option GraphicsSpacing -> `1` should be a
number equal or greater than 0 or a list of two such numbers; substituting 0 instead.";

StackedGraphicsArray::aspr = "Warning: Aspect ratios of the given plots are not identical. The aspect ratio of the first plot, `1`, will override all other settings.";

Implementation

This part contains the actual definitions and any auxiliary functions that should not be visible outside.

Begin the private context (implementation part)

Begin["`Private`"]

Definition of auxiliary functions and local (static) variables

arraySpacing[]

Determine horizontal and vertical array spacing. The value of the GraphicsSpacing option should be a
number equal or greater than 0 or a list of two such numbers:

arraySpacing[spacing_] := (Message[StackedGraphicsArray::arrsp, spacing]; {0, 0})

arraySpacing[spacing_?(# >= 0 &)] := {spacing, spacing};

arraySpacing[spacing:{hsp_?(# >= 0 &), vsp_?(# >= 0 &)}] := spacing;

defaultTickLabels[]

Create two-dimensional array with elements {bottom,left,top,right} where each element is either None (labels to be removed) or Automatic (labels to be kept):

defaultTickLabels[plots_?VectorQ, spacing_] := 
    First[defaultTickLabels[{plots}, spacing]];

defaultTickLabels[plots_?MatrixQ, spacing_] :=
    Module[{dim, ft, s},
        dim = Dimensions[plots];
        ft = Table[Automatic, {dim[[1]]}, {dim[[2]]}, {4}];
        s = Flatten[{spacing}];
        If[First[s] == 0,
            Do[ft[[i, j, 2]] = None, {i, 1, dim[[1]]}, {j, 2, dim[[2]]}];
            Do[ft[[i, j, 4]] = None, {i, 1, dim[[1]]}, {j, 1, dim[[2]]-1}];
        ];
        If[Last[s] == 0,
            Do[ft[[i, j, 1]] = None, {i, 1, dim[[1]]-1}, {j, 1, dim[[2]]}];
            Do[ft[[i, j, 3]] = None, {i, 2, dim[[1]]}, {j, 1, dim[[2]]}];
        ];
        ft
    ];

determineAspectRatio[]

Determine aspect ratio of the given plots. If the aspect ratio is not the same for all plots, a warning is issued.

determineAspectRatio[plots_] :=
    Module[{aspectRatios, aspectRatio},
        aspectRatios = Flatten[
            Map[
                If[# =!= None,
                    (AspectRatio /. AbsoluteOptions[#, AspectRatio]),
                    Sequence[]
                ]&,
                plots, {2}
            ]
        ];
        aspectRatio = First[aspectRatios];
        If[!(Equal @@ aspectRatios),
            Message[StackedGraphicsArray::aspr, aspectRatio]
        ];
        aspectRatio
    ];

removeTickLabels[]

Remove tick labels, but keep the actual tick marks:

removeTickLabels[None] := {};

removeTickLabels[plot_] :=
    FrameTicks /. AbsoluteOptions[plot, FrameTicks] /.
        {a_Real, b_, c_, d_} -> {a, "", c, d};

extractCorePlotRegion[]

This function extracts the "bare" plotting region with all labels stripped. Empty slots in the graphics array are represented by the symbol None and are ignored.

Before composing the Graphics objects, the plot range of each individual plot has to be changed to an absolute setting. Otherwise, PlotRange -> All will cause the plot to be scaled down as any Epilog primitives are also taken into account before rendering the plot.

extractCorePlotRegion[None, aspr_] := None;

extractCorePlotRegion[plot_, aspr_] :=
    Show[plot,
        DisplayFunction -> Identity, PlotLabel -> None,
        Frame -> True, FrameLabel -> None,
        FrameTicks -> removeTickLabels[plot],
        AbsoluteOptions[plot, PlotRange],
        AspectRatio -> aspr
    ];

computeBoundingBox[]

This function computes the lower left and upper right corner of the plot at position {xi, yi}:

computeBoundingBox[{xi_, yi_}, {hsp_, vsp_}, aspr_] := 
    {{xi * (1+hsp),     (-yi * (1+vsp)) * aspr},
     {xi * (1+hsp) + 1, (-yi * (1+vsp) + 1) * aspr}};

filterTicksInPlotRange[]

In special cases, Mathematica generates tick marks just outside the plot range (which are automatically removed when actually plotting the graphics object). To avoid any overlap of tick marks placed via Epilog between neighbouring graphics objects, we discard these tick marks:

filterTicksInPlotRange[ticks_List, {min_, max_}] :=
    Select[ticks, ((#[[1]] >= min) && (#[[1]] <= max))&];

filterTicksInPlotRange[plot_] :=
    Module[{ticks, xrange, yrange},
        ticks = FrameTicks /. AbsoluteOptions[plot, FrameTicks];
        {xrange, yrange} = PlotRange /. AbsoluteOptions[plot, PlotRange];
        {
            filterTicksInPlotRange[ticks[[1]], xrange],
            filterTicksInPlotRange[ticks[[2]], yrange],
            filterTicksInPlotRange[ticks[[3]], xrange],
            filterTicksInPlotRange[ticks[[4]], yrange]
        }
    ];

addTickLabelsScaled[]

Add tick labels indirectly via Text primitives (for use with Epilog):

addTickLabelsScaled[None, region_, labels_] := Sequence[];

addTickLabelsScaled[plot_, region:{{sxmin_, symin_}, {sxmax_, symax_}}, labels_]:=
    Module[{frameTickLabels, xmin, xmax, ymin, ymax, xr, yr},
        frameTickLabels = filterTicksInPlotRange[plot];
        frameTickLabels = Map[Select[#, (#[[2]] =!= "")&]&, frameTickLabels];

        {{xmin, xmax}, {ymin, ymax}} = PlotRange /. AbsoluteOptions[plot, PlotRange];
        xr = (sxmax - sxmin) / (xmax - xmin);
        yr = (symax - symin) / (ymax - ymin);
        
        frameTickLabels[[1]] =
            If[labels[[1]] === None, {},
                Text[#[[2]], {(#[[1]] - xmin) * xr + sxmin, symin - 0.02}, {0, 1}]& /@
                    frameTickLabels[[1]]];
        frameTickLabels[[2]] =
            If[labels[[2]] === None,{},
                Text[#[[2]], {sxmin - 0.02, (#[[1]] - ymin) * yr + symin}, {1, 0}]& /@
                    frameTickLabels[[2]]];
        frameTickLabels[[3]] =
            If[labels[[3]] === None, {},
                Text[#[[2]], {(#[[1]] - xmin) * xr + sxmin, symax + 0.02}, {0, -1}]& /@
                    frameTickLabels[[3]]];
        frameTickLabels[[4]] =
            If[labels[[4]] === None, {},
                Text[#[[2]], {sxmax + 0.02, (#[[1]] - ymin) * yr + symin}, {-1, 0}]& /@
                    frameTickLabels[[4]]];

        frameTickLabels
    ];

addFrameLabelsScaled[]

Add frame labels indirectly via Text primitives (for use with Epilog). The RotateLabel option of each plot is used to decide how to place the vertical labels:

addFrameLabelsScaled[None, region_, labels_] := Sequence[];

addFrameLabelsScaled[plot_, region:{{sxmin_, symin_}, {sxmax_, symax_}}, labels_] :=
    Module[{frameLabels, rotateLabels, sxd, syd},
        frameLabels = FrameLabel /. AbsoluteOptions[plot, FrameLabel];
        rotateLabels = RotateLabel /. AbsoluteOptions[plot, RotateLabel] /. 
        	{True -> {0,1}, _ -> Sequence[]};

        sxd = (sxmax - sxmin);
        syd = (symax - symin);
        
        If[Length[frameLabels] =!= 2 && Length[frameLabels] =!= 4,
            frameLabels = {}, (* i.e., frameLabels === None *)
            If[Length[frameLabels] == 2 || Length[frameLabels] == 4,
                frameLabels[[1]] =
                    If[(labels[[1]] === None) || (frameLabels[[1]] === None), {},
                        Text[frameLabels[[1]], {sxmin + 0.5 sxd, symin - 0.15 syd}, {0, 1}]];
                frameLabels[[2]] =
                    If[(labels[[2]] === None) || (frameLabels[[2]] === None), {},
                        Text[frameLabels[[2]], {sxmin - 0.15 sxd, symin + 0.5 syd}, {1, 0},
                            rotateLabels]];
            ];
            If[Length[frameLabels] == 4,
                frameLabels[[3]] =
                    If[(labels[[3]] === None) || (frameLabels[[3]] === None), {},
                        Text[frameLabels[[3]], {sxmin + 0.5 sxd, symin + 1.15 syd}, {0, -1}]];
                frameLabels[[4]] =
                    If[(labels[[4]] === None) || (frameLabels[[4]] === None), {},
                        Text[frameLabels[[4]], {sxmin + 1.15 sxd, symin + 0.5 syd}, {-1, 0},
                            rotateLabels]];
            ]
        ];
        frameLabels
    ];

addTickLabels[] - deprecated

Add tick labels indirectly via Text primitives (for use with Epilog). This function is only used by StackedGraphicsArray when invoked with CompositionMode -> 1 and might be removed in future versions of this package:

addTickLabels[None, labels_] := Sequence[];

addTickLabels[plot_, labels_]:=
    Module[{frameTickLabels, xmin, xmax, ymin, ymax},
        frameTickLabels = filterTicksInPlotRange[plot];
        frameTickLabels = Map[Select[#, (#[[2]] =!= "")&]&, frameTickLabels];
        {{xmin, xmax}, {ymin, ymax}} = PlotRange /. AbsoluteOptions[plot, PlotRange];
        frameTickLabels[[1]] =
            If[labels[[1]] === None, {},
                Text[#[[2]], Scaled[{0, -0.02}, {#[[1]], ymin}], {0, 1}]& /@
                    frameTickLabels[[1]]];
        frameTickLabels[[2]] =
            If[labels[[2]] === None,{},
                Text[#[[2]], Scaled[{-0.02, 0}, {xmin, #[[1]]}], {1, 0}]& /@
                    frameTickLabels[[2]]];
        frameTickLabels[[3]] =
            If[labels[[3]] === None, {},
                Text[#[[2]], Scaled[{0, 0.02}, {#[[1]], ymax}], {0, -1}]& /@
                    frameTickLabels[[3]]];
        frameTickLabels[[4]] =
            If[labels[[4]] === None, {},
                Text[#[[2]], Scaled[{0.02, 0}, {xmax, #[[1]]}], {-1, 0}]& /@
                    frameTickLabels[[4]]];
        frameTickLabels
    ];

addFrameLabels[] - deprecated

Add frame labels indirectly via Text primitives (for use with Epilog). The RotateLabel option of each plot is used to decide how to place the vertical labels. This function is only used by StackedGraphicsArray when invoked with CompositionMode -> 1 and might be removed in future versions of this package:

addFrameLabels[None, labels_] := Sequence[];

addFrameLabels[plot_, labels_] :=
    Module[{frameLabels, rotateLabels},
        frameLabels = FrameLabel /. AbsoluteOptions[plot, FrameLabel];
        rotateLabels = RotateLabel /. AbsoluteOptions[plot, RotateLabel] /. 
            {True -> {0,1}, _ -> Sequence[]};
        If[Length[frameLabels] =!= 2 && Length[frameLabels] =!= 4,
            frameLabels = {}, (* frameLabels === None *)
            If[Length[frameLabels] == 2 || Length[frameLabels] == 4,
                frameLabels[[1]] =
                    If[(labels[[1]] === None) || (frameLabels[[1]] === None), {},
                        Text[frameLabels[[1]], Scaled[{0.5, -0.15}], {0, 1}]];
                frameLabels[[2]] =
                    If[(labels[[2]] === None) || (frameLabels[[2]] === None), {},
                        Text[frameLabels[[2]], Scaled[{-0.15, 0.5}], {1, 0}, rotateLabels]];
            ];
            If[Length[frameLabels] == 4,
                frameLabels[[3]] =
                    If[(labels[[3]] === None) || (frameLabels[[3]] === None), {},
                        Text[frameLabels[[3]], Scaled[{0.5, 1.15}], {0, -1}]];
                frameLabels[[4]] =
                    If[(labels[[4]] === None) || (frameLabels[[4]] === None), {},
                        Text[frameLabels[[4]], Scaled[{1.15, 0.5}], {-1, 0}, rotateLabels]];
            ]
        ];
        frameLabels
    ];

Definition of the exported functions

StackedGraphicsArray[]

Options[StackedGraphicsArray] =
    Join[Options[GraphicsArray], {StackedLabel -> Automatic, CompositionMode -> 2}];
SetOptions[StackedGraphicsArray, GraphicsSpacing -> 0];

When calling StackedGraphicsArray[] with just a vector of plots, all plots are drawn adjacent to each other (i.e., a matrix consisting of one single row). If the StackedLabel option is given, it needs to be converted to the new matrix layout as well:

StackedGraphicsArray[plots_?VectorQ, 
	opts1___?OptionQ, StackedLabel -> labels_?MatrixQ, opts2___?OptionQ] :=
    	StackedGraphicsArray[{plots}, opts1, StackedLabel -> {labels}, opts2];

StackedGraphicsArray[plots_?VectorQ, opts___?OptionQ] :=
    StackedGraphicsArray[{plots}, opts];

"Empty slots" are represented by the symbol None. After composing the graphics array, Rectangles containing None are replaced by empty lists to avoid any warnings.

StackedGraphicsArray[plots_, opts___?OptionQ] :=
    Module[{hsp, vsp, labels, numx, numy, aspr, aspr2, plots2, bboxes},
        {hsp, vsp} = arraySpacing[
            GraphicsSpacing /. {opts} /. Options[StackedGraphicsArray]];

        labels = StackedLabel /. {opts} /. Options[StackedGraphicsArray];
        If[labels === Automatic, labels = defaultTickLabels[plots, {hsp, vsp}]];
        If[Length[Dimensions[labels]] =!= 3 ||
                Take[Dimensions[labels], 2] =!= Dimensions[plots],
            Message[StackedGraphicsArray::labels];
            Return[$Failed]
        ];

        {numy, numx} = Dimensions[plots];
        aspr = determineAspectRatio[plots];
        aspr2 = (numy + (numy - 1) * vsp) / (numx + (numx - 1) * hsp) * aspr;
        
        plots2 = Map[extractCorePlotRegion[#, aspr]&, plots, {2}];
        bboxes = Table[
            computeBoundingBox[{i, j}, {hsp, vsp}, aspr],
            {j, 0, numy-1},
            {i, 0, numx-1}
        ];
        
        Graphics[
            MapThread[ Rectangle[Sequence @@ #2, #1]&, {plots2, bboxes}, 2] 
                /. Rectangle[_, _, None] -> {},
            Epilog -> {
                MapThread[
                    addTickLabelsScaled[#1, #2, #3]&,
                    {plots, bboxes, labels}, 2],
                MapThread[
                    addFrameLabelsScaled[#1, #2, #3]&,
                    {plots, bboxes, labels}, 2],
                Epilog /. {opts} /. (Epilog -> {})
            },
            FilterOptions[Graphics, opts],
            PlotRange -> All,
            AspectRatio -> aspr2
        ]
    ];

The following function is provided for backwards compatibility with versions 1.x of the package. It is only used by StackedGraphicsArray when invoked with CompositionMode -> 1 and might be removed in future versions of this package: Its use is strongly discouraged.

Implementation of "empty slots": To avoid any erroneous warnings, we use the following trick. The symbol None is first replaced by Graphics[ ] (at the top level only). MapThread then produces "empty" Graphics objects for these slots, i.e. a Graphics object containing only options. These option-only Graphics are then replaced by completely empty Graphics objects (otherwise Show issues warnings).

StackedGraphicsArray[plots_, opts___?OptionQ] /;
    (CompositionMode == 1 /. {opts} /. Options[StackedGraphicsArray]) :=
    Module[{spacing, labels, plots2, aspr},
        spacing = GraphicsSpacing /. {opts} /. Options[StackedGraphicsArray];
        labels = StackedLabel /. {opts} /. Options[StackedGraphicsArray];
        If[labels === Automatic, labels = defaultTickLabels[plots, spacing]];
        If[Length[Dimensions[labels]] =!= 3 ||
                Take[Dimensions[labels], 2] =!= Dimensions[plots],
            Message[StackedGraphicsArray::labels];
            Return[$Failed]
        ];
        aspr = determineAspectRatio[plots];

        plots2 = MapThread[
            Show[Replace[#1, None -> Graphics[], {0}],
                DisplayFunction -> Identity, PlotLabel -> None,
                Frame -> True, FrameLabel -> None,
                FrameTicks -> removeTickLabels[#1],
                If[#1 =!= None, AbsoluteOptions[#1, PlotRange], PlotRange -> Automatic],
                AspectRatio -> aspr,
                Epilog -> {addTickLabels[#1, #2], 
                    addFrameLabels[#1, #2],
                    Epilog /. Options[#1] /. (Epilog -> Sequence[])}
            ]&,
            {plots, labels}, 2];

        GraphicsArray[plots2 /. Graphics[{___}] -> Graphics[{}],
            FilterOptions[GraphicsArray, opts],
            GraphicsSpacing -> spacing,
            PlotRegion -> {{0.15, 0.85}, {0.15, 0.85}}]
    ];

End the private context

End[ ]

Epilog

This section protects exported symbols and ends the package.

Protect exported symbol

Protect[Evaluate[$Context <> "*"]]

End the package context

EndPackage[ ]

Examples, Tests

Needs["Graphics`StackedGraphicsArray`"];

p1 = 
        Plot[Sin[x], {x, 0, 2 Pi}, Frame -> True, 
          FrameLabel -> {"x", "Sin(x)"}, 
          DisplayFunction -> Identity, 
          Epilog -> {Text["Sin(x)", {1.5  π, 
                  0.5}]}];

p2 = 
        Plot[Sin[2 x], {x, 0, 2 Pi}, Frame -> True, 
          FrameLabel -> {"x",  "Sin(x)"}, 
          DisplayFunction -> Identity];

Frame and tick labels might overlap unless the graphic size is increased :

Show[StackedGraphicsArray[{p1, p2}], 
        ImageSize -> 500];

The following statement will only work correctly up to Mathematica 4.2. The option CompositionMode -> 1 is  provided for backwards compatibility and might be removed in future versions of this package:

Show[
        StackedGraphicsArray[{p1, p2},  
          CompositionMode  ->  1]];

Show[
        StackedGraphicsArray[{{p1, p2}}, 
          Epilog -> {Text["Trigonometric functions", {1, 
                  0.7}]}]];

Show[
        StackedGraphicsArray[{p1, p2}, 
          StackedLabel -> {{None, Automatic, None, None}, {None, None, 
                None, None}}]];

Show[StackedGraphicsArray[{{p1}, {p2}}]];

Show[
        StackedGraphicsArray[{{p1}, {p2}}, 
          StackedLabel -> {{{None, None, None, None}}, {{Automatic, 
                  None, None, None}}}]];

Show[
        StackedGraphicsArray[{{p1, p2, p1}, {p2, None, p2}}]];

Show[
        StackedGraphicsArray[{{p1, p2}, {p2, p1}}, 
          GraphicsSpacing -> {0, 0.5}]];

Show[
        StackedGraphicsArray[{{p1, p2}, {p2, p1}}, 
          GraphicsSpacing -> 0.5]];

Starting with version 1.6 of Graphics`StackedGraphicsArray`, plots generated with PlotRange -> All are handled correctly:

p3 = 
        Plot[Cos[x],  {x, 0, 2 Pi},  Frame -> True, 
          PlotRange -> All, 
          DisplayFunction -> Identity];

Show[StackedGraphicsArray[{{p1}, {p3}}]];

Plots with different aspect ratios are adjusted so that combined plots all have the same aspect ratio:

p4 = 
        Plot[Cos[2 x],  {x, 0, 2 Pi},  Frame -> True, 
          AspectRatio  -> 1, 
          DisplayFunction -> Identity];

Show[StackedGraphicsArray[{p4, p3}]];

The following two plots test StackedGraphicsArray's capability to discard tick marks that are located outside of the plot range (Mathematica 4.x generates a hidden tick mark at y = 1.25):

p1 = 
        Plot[Sin[x],  {x, 0, 1},  Frame -> True,  
          DisplayFunction -> Identity, 
          PlotRange -> {-0.6251,  1.2442}];

p2 = 
        Plot[Cos[x],  {x, 0, 1},  Frame -> True,  
          DisplayFunction -> Identity, 
          PlotRange -> {-0.6251,  1.2442}];

Show[StackedGraphicsArray[{{p1}, {p2}}]];