Normalize curves in a plot by the area under the curves

In the MWE below, there are two tables, with one (common) column of x values and two columns of y values (A and B) in each table. They are plotted in the same plot, after normalization to the maximum value of column B for each table (there is also an offset added).

How can I replace the normalization to the maximum value of column B for each table with the area between column B and column A? In other words: I would like to normalize to the area between B and A for each set of two curves. How can that be achieved?

Although numerically not very complicated, I do not know how to implement it in pgfplots (which would eliminate the need to throw the data in and out of Matlab or Octave each time I do this plot).

MWE:

\documentclass{article}
\usepackage[tightpage,active]{preview}
\usepackage{pgfplotstable}

\newcommand{\offset}{1}

\newcommand{\findmax}[3]{
\pgfplotstablesort[sort key={#2},sort cmp={float >}]{\sorted}{#1}%
\pgfplotstablegetelem{0}{#2}\of{\sorted}%
\pgfmathsetmacro#3{\pgfplotsretval}
}

X A B
0 1 5
1 1 7
2 2 7
3 2 5
4 2 3
}\tableone
\findmax{\tableone}{B}{\Bmaxone}

X A B
0 10 24
1 13 45
2 24 66
3 26 33
4 26 27
}\tabletwo
\findmax{\tabletwo}{B}{\Bmaxtwo}

\begin{document}
\begin{preview}
\begin{tikzpicture}
\begin{axis}

table[x=X,y expr=\thisrow{A}/\Bmaxone] {\tableone};
table[x=X,y expr=\thisrow{B}/\Bmaxone] {\tableone};

table[x=X,y expr=\thisrow{A}/\Bmaxtwo+\offset] {\tabletwo};
table[x=X,y expr=\thisrow{B}/\Bmaxtwo+\offset] {\tabletwo};

\end{axis}
\end{tikzpicture}
\end{preview}
\end{document}

• Are your datapoints always going to be equally spaced with a step length of 1?
– Jake
Feb 12, 2013 at 20:02
• The graphs that I want to make right now all have exactly the same X vectors, and the data points are equally spaced (although not with a step length of 1). Therefore, the answer that you posted will work as a solution for me now. I guess that making a more general solution where the X vector (column) is also an input that could deal with sets of graphs that have different step lengths would not be too hard, but making a function that could deal with uneven step lengths would of course be the most general. I guess that might require a different approach, with a weighted cumsum calculation. Feb 12, 2013 at 22:47
• Btw, I have arbitrary units on the y axis -- that is why the actual value of the equidistant step length does not matter. Is it possible to call elements from two columns at once (and also more than one element from each column at once) with something similar to \pgfplotstableforeachcolumnelement? That could be a start to an integration function that would work for variable step sizes. Feb 12, 2013 at 22:51

Calculating the area under the curves requires a bit of looping, but it's reasonably straightforward.

Note that if you have a lot of numbers, or the values are large (summing to more than about 16000), you'll need to use the fpu library, as in the following example.

\documentclass{article}
\usepackage{pgfplotstable}

\newcommand{\calcarea}[3]{
\pgfplotstablegetrowsof{#1}
\pgfmathtruncatemacro\numrows{\pgfplotsretval-1}
\pgfkeys{/pgf/fpu=true}
\def\cumsum{0}
\pgfplotstableforeachcolumnelement{#2}\of{#1}\as\elem{
\pgfmathparse{\cumsum+\elem}
\def\cumsum{\pgfmathresult}
}
\pgfplotstablegetelem{0}{#2}\of{#1}%
\pgfmathparse{2*\cumsum-\pgfplotsretval}
\def\cumsum{\pgfmathresult}
\pgfplotstablegetelem{\numrows}{#2}\of{#1}
\pgfmathparse{(\cumsum-\pgfplotsretval)/2}
\pgfmathfloattofixed{\pgfmathresult}
\edef#3{\pgfmathresult}
\pgfkeys{/pgf/fpu=false}
}

X A B
0 1 5
1 1 7
2 2 7
3 2 5
4 2 3
}\tableone

X A B
0 10 2400
1 13 4500
2 24 6600
3 26 3300
4 26 2700
}\tabletwo

\begin{document}

\calcarea{\tableone}{A}{\areaA}

\calcarea{\tableone}{B}{\areaB}

\calcarea{\tabletwo}{A}{\areaAtwo}

\calcarea{\tabletwo}{B}{\areaBtwo}

\begin{tikzpicture}
\begin{axis}

table[x expr=\coordindex,y expr=\thisrow{A}/abs(\areaA-\areaB)] {\tableone};
table[x expr=\coordindex,y expr=\thisrow{B}/abs(\areaA-\areaB)] {\tableone};

• @hjb981: If the area gets larger than the values plain TeX can handle, you'll need to use the fpu library. I've edited my answer.