Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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}
share|improve this question
    
Do you intend to include each plot from a separate .tex file, or are all of the sources meant to be in one place? This makes a difference to the best approach. (For example, as I have to have 'stand alone' versions of each plot, I create them as separate .tex files.) –  Joseph Wright Oct 14 '11 at 19:44
    
@JosephWright I intend to have the preamble changed to a package. For single charts I would include straight into the text. For lengthy multiple ones as for example tex.stackexchange.com/questions/31525/… are in separate files. –  Yiannis Lazarides Oct 14 '11 at 19:54
    
My suggestion would be to use pgfkeys to define a couple of keys - one for each of your parameters. Defining your parameters by means of a key-value approach will probably simplify the usage while it does not complicate the implementation. This would also allow you to write \XXXset{month=<common month>} in front of a couple of diagrams to set the shared parameters only once. An introduction to pgfkeys can be found in tug.org/TUGboat/tb30-1/tb94wright-keyval.pdf –  Christian Feuersänger Oct 15 '11 at 20:11
add comment

1 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}
share|improve this answer
    
Thanks for the style. I am trying to provide a better author environment and a consistent interface between LaTeX and the charts, as the people using it only know basic LaTeX commands. If you see my example the tikzpicture and axis disappear. But thanks again you gave me some good ideas to simplify the program. –  Yiannis Lazarides Oct 23 '11 at 16:34
    
@Yiannis Lazarides Your users have to learn a new syntax if it is your macro set or if it is the very subset of pgfplots presented in my first use case. –  quinmars Oct 23 '11 at 17:11
    
@quinmars I'm getting "Couldnt parse input W1 as a floating point number" error at line 44 from the complete code. What I'm missing? –  Ricardo Cristian Ramirez Feb 22 at 18:19
1  
@RicardoCristianRamirez, my example works here for pdflatex, xelatex and lualatex. Indeed it I get some problems with dvipdfm.exe, latex builds though without a problem. Maybe you have an old tikz or pgfplots version? –  quinmars Feb 22 at 20:52
    
@quinmars Thanks for feedback. It seems my pgfpicture package is old as you said. I am trying to update it in ubuntu. –  Ricardo Cristian Ramirez Feb 22 at 20:59
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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