2

I am having trouble in setting the width of a pgfplot to the page width. I found this post where the issue is discussed in detail, but the provided answer does not work with external and the other solutions scale the fonts. I'm trying to insert a pgfplot to my document that consists of two plots. This is the code

\documentclass{standalone}
\usepackage{pgfplots}
\usepgfplotslibrary{groupplots}

\begin{document} \begin{tikzpicture}[] \begin{groupplot}[group style={group name=myplot, group size= 2 by 1, horizontal sep=2.5em}, width=1\textwidth, every axis plot/.append style={line width=0.4mm, line join=round}, xmin=-1,xmax=1, ymin=-1,ymax=1., ] \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^2}; \end{groupplot}
\end{tikzpicture} \end{document}

I have used the layouts package to determine the linewidth and textwidth parameters using:

linewidth: \printinunitsof{cm}\prntlen{\linewidth}
textwidth: \printinunitsof{cm}\prntlen{\textwidth}

and they're both 11.1484cm. The problem is that the image exceeds these dimensions when I use width=1\textwidth. I could manually tune the image to occupy the width I want, but I thought there has to be a better way. It is not possible that something seemingly so simple is so hard to achieve. The solution provided should be general in the sense that should be applicable to an arbitrary number of plots in the groupplot. Fonts should not be scaled but should keep the same size as the rest of the document. It is also important that it works with \usetikzlibrary{external} because I work with a lot of figures.

aaragon
  • 3,041
  • It seems to me that the width refers to a single plot only. I don't really know why that is though. In that case, I would suggest just setting the width to .5\textwidth and check with \usepackage[showframe]{geometry} if this works in your particular case. – Markus G. Apr 29 '21 at 11:33
  • I tried with .5\textwidth but it's smaller than the full width. – aaragon Apr 29 '21 at 11:35
  • The standalone is the MWE, of course the image has to be included in a document where those are properly defined. In mine there were 11.1484cm. – aaragon Apr 29 '21 at 15:05
  • When computing the width of a plot pgfplots includes the expected width of the axis label, which is missing in this case. – John Kormylo Apr 29 '21 at 15:06
  • @JohnKormylo you mean by providing width=number? – aaragon Apr 29 '21 at 15:07
  • As opposed to [scale only axis]. – John Kormylo Apr 29 '21 at 15:19

3 Answers3

4

This is merely guesswork. However, it implements some function harmonicnumber, so it might not be entirely useless. Also in the example at hand it sort of works. But one has to "guess" some parameters, and it is more than likely that it will not generally work, but possibly by adjusting guessed width you may get acceptable results. The cleaner way is probably to understand what pgfplots does internally, or to follow John Kormylo's approach.

Yet this is what I got. The calc library and the lines

\path let \p1=($(current bounding box.east)-(current bounding box.west)$)
 in \pgfextra{\typeout{width used=\x1, width available=\the\textwidth}};

are just to check how efficient the space is used.

\documentclass{article}
\usepackage[showframe]{geometry}
\usepackage{pgfplots}
\usetikzlibrary{calc}
\usepgfplotslibrary{groupplots}
\pgfplotsset{compat=1.17}
\makeatletter
\pgfmathdeclarefunction*{harmonicnumber}{2}{%
\begingroup%
\pgfmathtruncatemacro\pgfutil@tmpa{#1}%
\ifnum\pgfutil@tmpa>1\relax
\pgfmathsetmacro\pgfutil@tmpb{harmonicnumber(\pgfutil@tmpa-1,#2)}%
\pgfmathparse{\pgfutil@tmpb+1/pow(\pgfutil@tmpa,#2)}%
\else
\pgfmathparse{1}%
\fi
\pgfmathsmuggle\pgfmathresult\endgroup%
}
\pgfplotsset{guess width/.code={%
\pgfmathsetmacro\pgfutil@tmpw{harmonicnumber(\pgfplots@group@columns-1,3)*\pgfkeysvalueof{/pgfplots/guessed offset}+\textwidth/\pgfplots@group@columns}%
\pgfplotsset{width=\pgfutil@tmpw pt}%
},guessed offset/.initial=15pt}
\makeatother
\begin{document}
\noindent
\begin{tikzpicture}[]
\begin{groupplot}[group style={group name=myplot, group size= 2 by 1, 
    horizontal sep=2.5em},guess width,  
    every axis plot/.append style={line width=0.4mm, line join=round},
      xmin=-1,xmax=1,
      ymin=-1,ymax=1.,
]
\nextgroupplot[]
\addplot + [mark=none, domain=-1:1] {x};
\nextgroupplot[]
\addplot + [mark=none, domain=-1:1] {x^2};
\end{groupplot}    
\path let \p1=($(current bounding box.east)-(current bounding box.west)$)
 in \pgfextra{\typeout{width used=\x1, width available=\the\textwidth}};
\end{tikzpicture}

\noindent \begin{tikzpicture}[] \begin{groupplot}[group style={group name=myplot, group size= 3 by 1, horizontal sep=2.5em},guess width,
every axis plot/.append style={line width=0.4mm, line join=round}, xmin=-1,xmax=1, ymin=-1,ymax=1., ] \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^2}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^3}; \end{groupplot}
\path let \p1=($(current bounding box.east)-(current bounding box.west)$) in \pgfextra{\typeout{width used=\x1, width available=\the\textwidth}}; \end{tikzpicture}

\noindent \begin{tikzpicture}[] \begin{groupplot}[group style={group name=myplot, group size= 4 by 1, horizontal sep=2.5em},guess width,
every axis plot/.append style={line width=0.4mm, line join=round}, xmin=-1,xmax=1, ymin=-1,ymax=1., ] \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^2}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^3}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^4}; \end{groupplot}
\path let \p1=($(current bounding box.east)-(current bounding box.west)$) in \pgfextra{\typeout{width used=\x1, width available=\the\textwidth}}; \end{tikzpicture}

\end{document}

enter image description here

1

This solution runs the plot twice, using the actual width of the first run to adjust the width of the second.

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{groupplots}
\usepackage{showframe}

\newsavebox{\tempbox}

\begin{document} \savebox{\tempbox}{\begin{tikzpicture} \begin{groupplot}[group style={group name=myplot, group size= 2 by 1, horizontal sep=2.5em}, width=0.5\textwidth, every axis plot/.append style={line width=0.4mm, line join=round}, xmin=-1,xmax=1, ymin=-1,ymax=1., ] \nextgroupplot[] %\addplot + [mark=none, domain=-1:1] {x}; \nextgroupplot[] %\addplot + [mark=none, domain=-1:1] {x^2}; \end{groupplot} \end{tikzpicture}}% measure width

\noindent\begin{tikzpicture}[] \begin{groupplot}[group style={group name=myplot, group size= 2 by 1, horizontal sep=2.5em}, width={\dimexpr \textwidth-0.5\wd\tempbox}, every axis plot/.append style={line width=0.4mm, line join=round}, xmin=-1,xmax=1, ymin=-1,ymax=1., ] \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^2}; \end{groupplot} \draw[red] (current bounding box.south west) rectangle (current bounding box.north east); \end{tikzpicture} \end{document}


This illustrates how one could enter the code only once using the groupwidth enviroment.

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{groupplots}
\usepackage{environ}
\usepackage{showframe}

\NewEnviron{groupwidth}[1]{% #1 = number of groupplots per row \tikzset{/pgfplots/width={\dimexpr \textwidth/#1}}% \sbox0{\BODY}% \tikzset{/pgfplots/width={\dimexpr 2\textwidth/#1 - \wd0/#1}}% \BODY}

\begin{document} \noindent\begin{groupwidth}{2} \begin{tikzpicture} \begin{groupplot}[group style={group name=myplot, group size= 2 by 1, horizontal sep=2.5em}, every axis plot/.append style={line width=0.4mm, line join=round}, xmin=-1,xmax=1, ymin=-1,ymax=1., ] \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^2}; \end{groupplot} \end{tikzpicture}% \end{groupwidth}

\end{document}


This version uses the aux file to communicate between runs. Instead of drawing the tikzpicture twice, you run the entire document at least two times.

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{groupplots}
\usepackage{showframe}

\newcommand{\newgroupwidth}[2]% #1 = id, #2 = width to use {\expandafter\xdef\csname groupwidth#1\endcsname{#2}}

\newcounter{groupwidth} \newsavebox{\groupwidthbox} \makeatletter \newenvironment{groupwidth}[1]% #1 = number of groupplots per row {\edef\groupnumber{#1}% save for \endgroupwidth \stepcounter{groupwidth}% @ifundefined{groupwidth\thegroupwidth}{\pgfmathsetlengthmacro{\mywidth}{\textwidth/\groupnumber}}% {\expandafter\let\expandafter\mywidth\csname groupwidth\thegroupwidth\endcsname}% \begin{lrbox}{\groupwidthbox}% \tikzset{/pgfplots/width={\mywidth}}% \ignorespaces}% {\end{lrbox}% \usebox\groupwidthbox \pgfmathsetlengthmacro{\mywidth}{\mywidth + (\textwidth - \wd\groupwidthbox)/\groupnumber} \immediate\write@auxout{\string\newgroupwidth{\thegroupwidth}{\mywidth}}} \makeatother

\begin{document} \noindent\begin{groupwidth}{2} \begin{tikzpicture} \begin{groupplot}[group style={group name=myplot, group size= 2 by 1, horizontal sep=2.5em}, every axis plot/.append style={line width=0.4mm, line join=round}, xmin=-1,xmax=1, ymin=-1,ymax=1., ] \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x}; \nextgroupplot[] \addplot + [mark=none, domain=-1:1] {x^2}; \end{groupplot} %\draw[red] (current bounding box.south west) rectangle (current bounding box.north east); \end{tikzpicture}% \end{groupwidth}

\end{document}

John Kormylo
  • 79,712
  • 3
  • 50
  • 120
  • Thanks for your answer. The solution works, but in my case it's quite intrusive and I would have to change way too much code (it's a book). All my tikz figures are individual .tex files in a figures folder. Do you have an alternative idea perhaps? – aaragon Apr 29 '21 at 18:23
  • Not unless one can find a pattern for how the actual width differs from the desired width. – John Kormylo Apr 29 '21 at 21:45
  • Would your solution work with \usetikzlibrary{external}? – aaragon Apr 30 '21 at 12:32
0

Consider this:

Create a file named groupPlot.tikz that includes the following content:

% groupPlot.tikz
\begin{tikzpicture}[]
\usepgfplotslibrary{groupplots}
\begin{groupplot}[group style={group name=myplot, group size= 2 by 1, horizontal sep=2.5em},
width=1\textwidth,
every axis plot/.append style={line width=0.4mm, line join=round},
      xmin=-1,xmax=1,
      ymin=-1,ymax=1.,
]
\nextgroupplot[width=0.5\textwidth]
\addplot + [mark=none, domain=-1:1] {x};
\nextgroupplot[width=0.5\textwidth]
\addplot + [mark=none, domain=-1:1] {x^2};
\end{groupplot}    
\end{tikzpicture}

Call for the tikzscale package in your main document, and use the includegraphics command, as in this example:

% main.tex
\documentclass{article}

\usepackage{tikz,pgfplots} \pgfplotsset{compat=newest} \usepackage{tikzscale} \usepgfplotslibrary{groupplots}

\usepackage[showframe]{geometry} % For visibility \usepackage{lipsum} \begin{document} \lipsum[1] \begin{figure} \centering \includegraphics[width=\textwidth,height=0.3\textwidth]{groupPlot.tikz} \caption{Caption} \label{fig:my_label} \end{figure}

\end{document}

Result:

Article document class

As yet another trial, used with the book documentclass, altered to tufte-book layout:

% main.tex
\documentclass{book}
\usepackage{tikz,pgfplots}
\pgfplotsset{compat=newest}
\usepackage{tikzscale}
\usepgfplotslibrary{groupplots}

\usepackage[a4paper,left=24.8mm,top=27.4mm,headsep=2\baselineskip,textwidth=107mm,marginparsep=8.2mm,marginparwidth=49.4mm,textheight=49\baselineskip,headheight=\baselineskip,asymmetric,showframe]{geometry} % Tufte book layout \usepackage{lipsum} \begin{document} \chapter*{First Chapter} \lipsum[1] \begin{figure} \centering \includegraphics[width=\textwidth,height=0.3\textwidth]{groupPlot.tikz} \caption{Caption} \label{fig:my_label} \end{figure} \end{document}

Book document class

The only message I get is

Package tikzscale Warning: Scaling of groupPlot.tikz's width was only accurate to 31.749pt on input line 16.

With tikzscale you can explicitly define the width-to-height aspect ratio. So you can change the height=0.3\textwidth option to what works best for you.

I only added a \nextgroupplot[width=0.5\textwidth] to your plots, after reading this answer.

tush
  • 1,115