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.

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:

enter image description here

\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}
}

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

\pgfplotstableread{
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}

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

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

\end{axis}
\end{tikzpicture}
\end{preview}
\end{document}
share|improve this question
    
Are your datapoints always going to be equally spaced with a step length of 1? –  Jake Feb 12 '13 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. –  hjb981 Feb 12 '13 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. –  hjb981 Feb 12 '13 at 22:51
add comment

1 Answer

up vote 7 down vote accepted

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}
}


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

\pgfplotstableread{
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}

\addplot [black]
table[x expr=\coordindex,y expr=\thisrow{A}/abs(\areaA-\areaB)] {\tableone};
\addplot [black] 
table[x expr=\coordindex,y expr=\thisrow{B}/abs(\areaA-\areaB)] {\tableone};
\addplot [red]
table[x=X,y expr=\thisrow{A}/abs(\areaAtwo-\areaBtwo)+0.5] {\tabletwo};
\addplot [red] 
table[x=X,y expr=\thisrow{B}/abs(\areaAtwo-\areaBtwo)+0.5] {\tabletwo};

\end{axis}
\end{tikzpicture}
\end{document}
share|improve this answer
    
There seems to be some kind of limitation in the size of data tables with some of the commands used here. The data files I currently work with have 242 rows (and 10 columns). If I truncate the data (reducing the number of rows), everything is fine, but if I input the whole data set, I get 101 errors that read: ! Dimension to large. <recently read> pgfmath@x –  hjb981 Feb 13 '13 at 0:01
    
@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. –  Jake Feb 13 '13 at 8:45
    
It works with my larger data sets now (values are in the thousands for each element, so the cumulative sum was well above 16000, so that seems to have been the limiting factor before). –  hjb981 Feb 13 '13 at 9:40
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.