[Tex/LaTex] How to generate templates for charts

pgfplotstikz-pgf

Most of us create charts that have to be repeated
a number of times with only the data varying. Applying DRY (do not repeat yourself) programming principles to such problems is not an easy task.
To achieve this, one has to separate the data from the presentation. This has the added advantages of providing better semantics for authors and can hide the verbosity of the pgf and friends libraries. It also makes progressive editing of the looks of the charts easier.

For example consider the charts shown below, the data part consists of the title, the month label and two data series:

enter image description here

These are produced using an environment as follows:

\begin{monthlyChart}
  \addTitle{AHU (October)}
  \addMonth{Oct-11}
  \addPlanned{10}{20}{30}{40}
  \addActual {9}{18}{31}{39}
\end{monthlyChart}

Question

How can I abstract the creation of the environment, i.e., to consider the original bar chart as a master template from which other variations can be defined — rather than repeating the process. For example, to create a slightly different chart, I have used the same environment as follows:

\begin{monthlyChart}
  \addTitle{Turnover $\times 10^3$}
  \addMonth{Jan-11}
  \addPlanned{300}{400}{500}{600}
  \addActual {300}{400}{500}{600}
\end{monthlyChart}

giving a chart that can now be used for a different purpose, resulting in:

enter image description here

I am looking for a way to define a createNewChartType sort of command, which will use a predefined template and adjust a few parameters, more like a relationship between a template and a theme as used in web development. Lengthy and verbose
minimal follows:

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{patterns}
\usepackage{pgfplots}
\makeatletter
\newcommand\progressChart[8]{
  \def\week##1##2{
    \footnotesize
    \begin{tabular}{c}##1\\
        ##2\\\end{tabular}
  }
\begin{tikzpicture}     
  \begin{axis}[
      width=6.9cm,height=5cm,
      bar width=0.3cm,
      title={\ctitle},
      ymin=0,
     % ytick={0,25,50,100,125},
      ylabel=Total,
      ybar=0,symbolic x coords={A, B, C, D}, 
      xtick=data,
      xticklabels={
         \week{\Month}{week-1},
         \week{\Month}{week-2}, 
         \week{\Month}{week-3},
         \week{\Month}{week-4}
      },enlarge x limits=true,
    legend style={
            at={(.43,.98)}, font=\footnotesize }], 
    \addplot[draw=none, fill=gray] coordinates { 
      (A,#1) 
      (B,#2) 
      (C,#3) 
      (D,#4)
     }; 
    \addplot[draw=none,fill=orange!90] coordinates { 
      (A,#5) 
      (B,#6) 
      (C,#7) 
      (D,#8)
      }; 
  \legend{Planned, Actual} 
  \end{axis}   
\end{tikzpicture}}

\newenvironment{monthlyChart}{%
  \def\addMonth##1{\def\Month{##1}}
  \def\addTitle##1{\def\ctitle{\small\textbf{##1}}}
  \def\addPlanned##1##2##3##4{%
  \def\one{##1}\def\two{##2}\def\three{##3}\def\four{##4}}
  \def\addActual##1##2##3##4{%
    \progressChart{\one}{\two}{\three}{\four}{##1}{##2}{##3}{##4}
   }% 
  \parindent0pt}{}
\makeatother
\begin{document}


\begin{monthlyChart}
  \addTitle{AHU (October)}
  \addMonth{Oct-11}
  \addPlanned{10}{20}{30}{40}
  \addActual {9}{18}{31}{39}
\end{monthlyChart}
\begin{monthlyChart}
  \addTitle{AHU (November)}
  \addMonth{Nov-11}
  \addPlanned{30}{40}{50}{60}
  \addActual {30}{40}{60}{60}
\end{monthlyChart}
\begin{monthlyChart}
  \addTitle{AHU (December)}
  \addMonth{Dec-11}
  \addPlanned{30}{40}{50}{60}
  \addActual {30}{50}{58}{70}
\end{monthlyChart}
\begin{monthlyChart}
  \addTitle{AHU (January)}
  \addMonth{Jan-11}
  \addPlanned{30}{40}{50}{60}
  \addActual {30}{40}{50}{113}
\end{monthlyChart}

\begin{monthlyChart}
  \addTitle{Turnover $\times 10^3$}
  \addMonth{Jan-11}
  \addPlanned{300}{400}{500}{600}
  \addActual {300}{400}{500}{600}
\end{monthlyChart}

\end{document}

Best Answer

There is a way to define a master template and it is not that difficult as you expected it to be. It is called Own Style and is one of the things that makes pgfplots such powerful. Unfortunately the section about defining own styles is very short compared to it importance, hence it is easily overlook (see section 4.17.2 in the pgfplots manual). Let's take a look on how it works:

\pgfplotsset{actual chart bar/.style={draw=none,fill=orange!90}}
\pgfplotsset{planned chart bar/.style={draw=none, fill=gray}}

Here we define the styles for the actual and planned chart bars. As you see we can use spaces in the style names (no need for untexish camel case ^^). The many work is done in the style for the axis we are going to use later. I will only comment the elements I have changed. Most of the stuff is taken verbatim from your example.

\pgfplotsset{monthly chart/.style={
    width=6.9cm,height=5cm,
    bar width=0.3cm,
    ymin=0,
    % ytick={0,25,50,100,125},
    ylabel=Total,
    ybar=0,symbolic x coords={W1, W2, W3, W4}, 
    xtick=data,
    xticklabels={
         \week{#1}{week-1},
         \week{#1}{week-2},
         \week{#1}{week-3},
         \week{#1}{week-4}
    },

I changed the symbolic coordinates to W1, W2, W3 and W4, because we are going to use them in the main document and I think it makes it more readable that way. The \week command is the same as yours. The important point here is the #1 it should be the month (e.g. Jan-11) and is different for every plot, hence we cannot use a constant. We will see later, how this #1 will become the actual month.

    x tick label style={font=\footnotesize},

Here we change the font size of the xticks labels.

    enlarge x limits=true,
    legend style={at={(.43,.98)}, font=\footnotesize },
    cycle list={
        planned chart bar,
        actual chart bar
    },

The cycle list will define the style for the single plots, i.e. the first plot gets the planned char bar style and the second plot the actual chart bar style.

    legend entries={Planned, Actual},

We can already define the legend here, if you need another order for a special chart you can override this setting in this chart.

    title style={font=\small\bfseries}
}}

We only define the style of the title. The title itself is set in the specific chart.

Now we can use the use the style for every monthly chart in the main document:

\begin{tikzpicture}
    \begin{axis}[monthly chart=Oct-11, title={AHU (October)}]
        \addplot coordinates {(W1,10) (W2,20) (W3,30) (W4,40)};
        \addplot coordinates {(W1,9) (W2,18) (W3,31) (W4,39)};
    \end{axis}
\end{tikzpicture}

The title is set in the same way as you would do it in any ordinary pgfplot. In general this looks very familiar to every one who has used pgfplots before. The only novel here is the monthly chart style. And now we also see how the #1 is replaced by the desired month/text. This approach is a little bit more verbose then your new environment, but it is very flexible and readable.

For example it is straight forward to insert a third data set:

\begin{tikzpicture}
    \begin{axis}[monthly chart=Nov-11, title={AHU (November)}]
        \addplot[planned chart bar] coordinates {(W1,14) (W2,20) (W3,50) (W4,60)};
        \addplot[draw=none, fill=green] coordinates {(W1,11) (W2, 22) (W3,33) (W4,43)};
        \addplot[actual chart bar] coordinates {(W1,15) (W2,21) (W3,60) (W4,60)};
        \legend{Planned,Ideal,Actual}
    \end{axis}
\end{tikzpicture}

Or if need to change the legend position:

\begin{tikzpicture}
    \begin{axis}[monthly chart=Dec-11, title={AHU (December)}, legend style={at={(.95,.98)}}]
        \addplot coordinates {(W1,65) (W2,40) (W3,40) (W4,36)};
        \addplot coordinates {(W1,75) (W2,58) (W3,50) (W4,30)};
    \end{axis}
\end{tikzpicture}

Or to change the ylabel:

\begin{tikzpicture}
    \begin{axis}[monthly chart=Jan-11, title={AHU (January)}, ylabel=Sold Apples]
        \addplot coordinates {(W1,30) (W2,40) (W3,50) (W4,60)};
        \addplot coordinates {(W1,30) (W2,40) (W3,50) (W4,113)};
    \end{axis}
\end{tikzpicture}

enter image description here

And here is the complete code:

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{patterns}
\usepackage{pgfplots}

\def\week#1#2{
    \begin{tabular}{c}#1\\
    #2\\\end{tabular}
    }

\pgfplotsset{actual chart bar/.style={draw=none,fill=orange!90}}
\pgfplotsset{planned chart bar/.style={draw=none, fill=gray}}

\pgfplotsset{monthly chart/.style={
        width=6.9cm,height=5cm,
        bar width=0.3cm,
        ymin=0,
        % ytick={0,25,50,100,125},
        ylabel=Total,
        ybar=0,symbolic x coords={W1, W2, W3, W4}, 
        xtick=data,
        xticklabels={
            \week{#1}{week-1},
            \week{#1}{week-2},
            \week{#1}{week-3},
            \week{#1}{week-4}
        },
        x tick label style={font=\footnotesize},
        enlarge x limits=true,
        legend style={at={(.43,.98)}, font=\footnotesize },
        cycle list={
            planned chart bar,
            actual chart bar
        },
        legend entries={Planned, Actual},
        title style={font=\small\bfseries}
    }
}

\begin{document}
\noindent
\begin{tikzpicture}
    \begin{axis}[monthly chart=Oct-11, title={AHU (October)}]
        \addplot coordinates {(W1,10) (W2,20) (W3,30) (W4,40)};
        \addplot coordinates {(W1,9) (W2,18) (W3,31) (W4,39)};
    \end{axis}
\end{tikzpicture}
\begin{tikzpicture}
    \begin{axis}[monthly chart=Nov-11, title={AHU (November)}]
        \addplot[planned chart bar] coordinates {(W1,14) (W2,20) (W3,50) (W4,60)};
        \addplot[draw=none, fill=green] coordinates {(W1,11) (W2, 22) (W3,33) (W4,43)};
        \addplot[actual chart bar] coordinates {(W1,15) (W2,21) (W3,60) (W4,60)};
        \legend{Planned,Ideal,Actual}
    \end{axis}
\end{tikzpicture}
\begin{tikzpicture}
    \begin{axis}[monthly chart=Dec-11, title={AHU (December)}, legend style={at={(.95,.98)}}]
        \addplot coordinates {(W1,65) (W2,40) (W3,40) (W4,36)};
        \addplot coordinates {(W1,75) (W2,58) (W3,50) (W4,30)};
    \end{axis}
\end{tikzpicture}
\begin{tikzpicture}
    \begin{axis}[monthly chart=Jan-11, title={AHU (January)}, ylabel=Sold Apples]
        \addplot coordinates {(W1,30) (W2,40) (W3,50) (W4,60)};
        \addplot coordinates {(W1,30) (W2,40) (W3,50) (W4,113)};
    \end{axis}
\end{tikzpicture}
\end{document}