The world’s leading publication for data science, AI, and ML professionals.

How to Create Publication-Ready Plots with LaTeX: Part II

Let's take a look at stacked plots!

Photo by Isaac Smith on Unsplash
Photo by Isaac Smith on Unsplash

Hello friends! So I’m back with another article on plotting with Latex! In case you have missed my previous article or would like to jog your memory a bit (I know it has been a while since my first article!), you can read it here.

In this article, I’ll be walking through one particular kind of plot: stacked plots (or, groupplots in LaTeX; I’ll be using these two terms interchangeably throughout this article). I find these plots to be really useful for many scenarios. However, each use case may demand different customizations and I won’t be able to cover all of them in a single article. So I thought that I’ll simply focus on how to get started with stacked plots in LaTeX. In particular, I’ll be detailing how I tackled one of the most frustrating challenges when I first started with this type of plot: adding legends to stacked plots!

So without further ado, let’s get started!

Stacked plots in LaTeX

Let’s use the same flights.csv dataset (credit: seaborn.pydata.org) from our previous article. However, this time we’ll plot the variation in monthly passenger count for 4 consecutive years (1949 – 1952). Using what we learnt last time, we can plot the scenario like this:

The LaTeX code to create this plot is given below.


begin{tikzpicture}
begin{axis}[
    myplotstyle,
    xtick=data,
    table/col sep=comma,
    xticklabels from table={Fig_groupplot_1.csv}{month},
    legend pos=outer north east,
    legend entries={1949,1950,1951,1952},
    xlabel={Month},
    ylabel={No. of passengers},
    x tick label style={rotate=90, /pgf/number format/.cd, set thousands separator={}},
]
addplot+[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_1.csv};
addplot+[smooth, color=black,mark=triangle*, mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_2.csv};
addplot+[smooth, color=black,mark=square*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_3.csv};
addplot+[smooth, color=black,mark=diamond*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_4.csv};
end{axis}
end{tikzpicture}

Although this plot looks good enough as it is, let’s check out how we can separate the plot for each year and stack them one upon the other (since that is what was promised in the title!).

We can use the groupplot option for doing this. In this case, we need to first replace the begin{axis}...end{axis} with begin{groupplot}...end{groupplot} . We’ll also need to define some basic styling options for this groupplot; some options I’ve included here are size of the groupplot, position of x- and y-axis labels and tickmarks, and vertical distance between individual plots in the groupplot. Next, we’ll need to add a command nextgroupplot before each addplot command. So the complete code will look something like this:

begin{tikzpicture}
begin{groupplot}[
        group style={
        group name=myplot,
        group size=1 by 4,
        xlabels at=edge bottom,
        xticklabels at=edge bottom,
        ylabels at=edge right,
        vertical sep=0pt,
    }, 
    myplotstyle,
    xtick=data,
    table/col sep=comma,
    xticklabels from table={Fig_groupplot_1.csv}{month},
    width=10cm, height=6cm,
    x tick label style={rotate=90, /pgf/number format/.cd},
    xlabel={Month},
    ylabel={No. of passengers},
]
nextgroupplot
addplot+[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_1.csv};

nextgroupplot
addplot+[smooth, color=black,mark=triangle*, mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_2.csv};

nextgroupplot
addplot+[smooth, color=black,mark=square*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_3.csv};

nextgroupplot
addplot+[smooth, color=black,mark=diamond*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_4.csv};
end{groupplot}
end{tikzpicture}

And the plot for the code above will look like this:

Now let’s move on to including legends in this groupplot. For our plot, there are three ways we can go about this:

Option 1: Legends inside each individual plot

For this one, we’ll simply add in a command addlegendentry{year} after each addplot and we’re done! Note: If you have multiple addplot commands within one plot, simply add the addlegendentry{year} command after each addplot .

The complete code will look like this:

begin{tikzpicture}
begin{groupplot}[
        group style={
        group name=myplot,
        group size=1 by 4,
        xlabels at=edge bottom,
        xticklabels at=edge bottom,
        ylabels at=edge right,
        vertical sep=0pt,
    }, 
    myplotstyle,
    xtick=data,
    table/col sep=comma,
    xticklabels from table={Fig_groupplot_1.csv}{month},
    width=10cm, height=6cm,
    x tick label style={rotate=90, /pgf/number format/.cd},
    xlabel={Month},
    ylabel={No. of passengers},
]
nextgroupplot
addplot+[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_1.csv}; 
addlegendentry{1949};

nextgroupplot
addplot+[smooth, color=black,mark=triangle*, mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_2.csv};
addlegendentry{1950};

nextgroupplot
addplot+[smooth, color=black,mark=square*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_3.csv};
addlegendentry{1951};

nextgroupplot
addplot+[smooth, color=black,mark=diamond*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_4.csv};
addlegendentry{1952};

end{groupplot}
end{tikzpicture}

Option 2: Add a text node within each plot to specify the corresponding year

For this, I changed all the plot markers to be the same. Then, we add a node to all the individual plots specifying the year for each of them. (Note: we did something similar for the mean plots we made in my previous article). This node can be defined something like this (You can read about node positioning using rel axis cs in more detail in my previous article here):

node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{Year}}

Here, I have also specified width of the node for convenience.

I find this to be the easier option in this particular case (where we have only a single column of data to be plotted in each plot). And this is how the complete code will look like for option 2:

begin{tikzpicture}
begin{groupplot}[
        group style={
        group name=myplot,
        group size=1 by 4,
        xlabels at=edge bottom,
        xticklabels at=edge bottom,
        vertical sep=0pt,
    }, 
    myplotstyle,
    xtick=data,
    table/col sep=comma,
    xticklabels from table={Fig_groupplot_1.csv}{month},
    width=10cm, height=6cm,
    x tick label style={rotate=90, /pgf/number format/.cd},
    xlabel={Month},
    ylabel={No. of passengers},
]
nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1949}};
addplot[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_1.csv};

nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1950}};
coordinate (top) at (rel axis cs:0,1);
addplot[smooth, color=black,mark=*, mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_2.csv};

nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1951}};
coordinate (top) at (rel axis cs:0,1);
addplot[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_3.csv};

nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1952}};
coordinate (top) at (rel axis cs:0,1);
addplot[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_4.csv};
end{groupplot}
end{tikzpicture}

Option 3: Combine all legend entries into a single legend box

In this case, I’ll be labelling all individual plots using label command like this: label{plots:plot1} , label{plots:plot2} , label{plots:plot3} , and label{plots:plot4} . Next, I’ll define the position where I want to place my legend box. This can be done by adding in the following lines of code after end{groupplot} but before end{tikzpicture} :

path (myplot c1r1.north west)--
      coordinate(legendpos)
      (myplot c1r1.north east);

Here, all we’re doing is creating a path spanning the two ends of the first plot (or, c1r1) in the groupplot (c1r1.north westc1r1.north east) and defining our legend position (legendpos) midway on this path. Note: thepath command draws an "invisible path". If you wish to see what you’re drawing before you place the nodes, just replace path with either path[draw] or draw command.

Once the position is defined, I’ll add in the legends. For this, I have once again used the nodecommand to do this (Or, you can use matrix , which is abbreviation for path node[matrix]; any of these commands will do the job). However, this time I have used a special option matrix of nodes in my node[] , which as the name implies specifies that I’ll need a matrix of nodes. The syntax for specifying this node is something like this:

node[matrix of nodes] at (position) { cell contents };

For this groupplot, I have defined the node as follows:

node[
    matrix of nodes,
    draw,
    inner sep=0.2em,
  ] at ([yshift=1em]legendpos)
  {
    ref{plots:plot1} & 1949 & [1em]
    ref{plots:plot2} & 1950 & [1em]
    ref{plots:plot3} & 1951 & [1em]
    ref{plots:plot4} & 1952};

A few node options that I have added in include draw (to make the legend box visible) and inner sep (to define the space between matrix outer boundary and content). I have also added a yshift to position the legend away from the plot boundaries. The cell contents are self-explanatory. Here is the complete code for this option:

begin{tikzpicture}
begin{groupplot}[
        group style={
        group name=myplot,
        group size=1 by 4,
        xlabels at=edge bottom,
        xticklabels at=edge bottom,
        ylabels at=edge right,
        vertical sep=0pt,
    }, 
    myplotstyle,
    xtick=data,
    table/col sep=comma,
    xticklabels from table={Fig_groupplot_1.csv}{month},
    width=10cm, height=6cm,
    x tick label style={rotate=90, /pgf/number format/.cd},
    xlabel={Month},
    ylabel={No. of passengers},
]
nextgroupplot
addplot+[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_1.csv}; label{plots:plot1}

nextgroupplot
addplot+[smooth, color=black,mark=triangle*, mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_2.csv}; label{plots:plot2}

nextgroupplot
addplot+[smooth, color=black,mark=square*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_3.csv}; label{plots:plot3}

nextgroupplot
addplot+[smooth, color=black,mark=diamond*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_4.csv}; label{plots:plot4}

end{groupplot}

path (myplot c1r1.north west)--
      coordinate(legendpos)
      (myplot c1r1.north east);

node[
    matrix of nodes,
    draw,
    inner sep=0.2em,
  ]at ([yshift=1em] legendpos)
  {
    ref{plots:plot1} & 1949 & [1em]
    ref{plots:plot2} & 1950 & [1em]
    ref{plots:plot3} & 1951 & [1em]
    ref{plots:plot4} & 1952};
end{tikzpicture}

Let’s take a look at all the three plot variations we just tried out:

Now, I would like to do one more modification to this plot to make them more aesthetically pleasing. Since we have the same label for the y-axis of every plot, let’s just use a single y-axis label for all of them. For this, we could simply remove the y-axis label from the groupplot style definitions and add in a node with the label centered vertically in the groupplot. We’ll do this by adding in the following lines of code:

    path (myplot c1r1.north west)
          -- (myplot c1r4.south west) 
          node[midway, xshift=-3em, rotate=90] 
          {textbf{No. of passengers}};

This is a variation of the path command we used before. Here, I’ll be drawing a path from the north west corner of the first plot (c1r1) to the south west corner of the last plot (c1r4) in the groupplot and placing a node midway on this path. Here, in addition to shifting the node outside the plot boundaries, I have also rotated it by 90 degrees. And the y-label is done!

This is what the complete code and plot will look like now. Note: For brevity, I’m only showing the code/plot for one of the plot variations mentioned before (option 2). The same path command will work for the other two plot options as well.

begin{tikzpicture}
begin{groupplot}[
        group style={
        group name=myplot,
        group size=1 by 4,
        xlabels at=edge bottom,
        xticklabels at=edge bottom,
        vertical sep=0pt,
    }, 
    myplotstyle,
    xtick=data,
    table/col sep=comma,
    xticklabels from table={Fig_groupplot_1.csv}{month},
    width=10cm, height=6cm,
    x tick label style={rotate=90, /pgf/number format/.cd},
    xlabel={Month},
]
nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1949}};
addplot[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_1.csv};

nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1950}};
addplot[smooth, color=black,mark=*, mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_2.csv};

nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1951}};
addplot[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_3.csv};

nextgroupplot[]
node [text width=5em] at (rel axis cs: 0.15,0.9){textbf{1952}};
addplot[smooth, color=black,mark=*,mark options={fill=white,scale=1.5}] table[x expr=coordindex,y=passengers, col sep=comma] {Fig_groupplot_4.csv};
end{groupplot}
    path (myplot c1r1.north west)
          -- (myplot c1r4.south west) node[midway, xshift=-3em, rotate=90] {textbf{No. of passengers}};
end{tikzpicture}

Thanks for reading! 🙂

And that’s a wrap… for this article!

As always, this is just the tip of the iceberg as far as groupplots are concerned. There are many more customizations and complicated stacked plots you can create with LaTeX, but this should hopefully suffice to get you started in the right direction. Please do drop me a comment on what you would like to see next with regards to plotting in LaTeX! So until the next time we meet…

Happy learning!


Related Articles