[Tex/LaTex] Adding values to pgfplot legend

gnuplotpgfplotstikz-pgf

As a follow-up to my earlier question (here), here is a question that is perhaps a little more latex than gnuplot. having used gnuplot and pgfplots to generate a fitted curve, is there an easy way to have latex automatically report the fit parameters (in this case: Ymax, EC50, nH) from the gnuplot fit in the legend or caption?

Here is a working example:

\documentclass[11pt]{article}
\usepackage{pgfplots}
\usepackage{filecontents}
\usepackage{xcolor}

\begin{filecontents}{drc1.dat}
2   17
5   55
10  96
20  125
50  144
100 147
200 147
500 146
\end{filecontents}



\begin{document}

\begin{figure}[h!t]
\centering
\begin{tikzpicture}

\begin{axis}[
    legend pos=north west,
    xmode=log,
    ymode=linear,
    axis x line*=bottom,
    axis y line*=left,
    tick label style={font=\small},
    grid=both,
    tick align=outside, 
    tickpos=left,
    xlabel= {[ACh]} (nM),
    ylabel=Response (mm),
   %%% GRAPH RANGE %%%
        xmin=0.1, xmax=1000,
        ymin=0, ymax=160,
   %%%%%%%%%%%%%%
        width=0.6\textwidth,
        height=0.4\textwidth,
    ]

\addplot[only marks, mark size=1.8, color=blue] file {drc1.dat};
    % Now call gnuplot to fit this data
    % The key is the raw gnuplot option
    % which allows to write a gnuplot script file
    \addlegendentry{{\tiny Experiment 1}}

\addplot+[raw gnuplot, draw=blue, mark=none, smooth] gnuplot {
    set log x;
    f(x)=Ymax/(1+(EC50/x)^nH);
    % let gnuplot fit, using column 1 and 2 of the data file
    % using the following initial guesses
    Ymax=150;
    nH=1;
    EC50=50;
     fit f(x) 'drc1.dat' using 1:2 via Ymax,EC50,nH;
         % Next, plot the function and specify plot range
         % The range should be approx. the same as the test.dat x range
     plot [x=0.1:1000] f(x);
       };       


\end{axis}
\end{tikzpicture}

\end{figure}

\end{document}

The TexShop console reports the results of the fit i.e.

Final set of parameters            Asymptotic Standard Error
=======================            ==========================

Ymax            = 146.818          +/- 3.228        (2.199%)
EC50            = 18.5506          +/- 0.9328       (5.028%)
nH              = 3.1713           +/- 0.5152       (16.25%)

which are also within the generated file fit.log. But are these accesible to latex?

Best Answer

You can use the gnuplot command set print "<filename" to open a new file, and then write the parameters into that file using print Ymax,EC50,nH. If you use set fit errorvariables;, the standard errors will be available as Ymax_err, EC50_err and so on. You can then use \pgfplotstableread{<filename>}<table macro> to read the file, and access the individual entries using \pgfplotstablegetelem{<row>}{<col>}\of<table macro>, which will save the entry into a temporary macro called \pgfplotsretval.

Here's an example of how to use this:

\documentclass[11pt]{article}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{filecontents}

\begin{filecontents}{drc1.dat}
2   17
5   55
10  96
20  125
50  144
100 147
200 147
500 146
\end{filecontents}



\begin{document}

\begin{figure}[h!t]
\centering
\begin{tikzpicture}

\begin{axis}[
    legend pos=north west,
    xmode=log,
    ymode=linear,
    axis x line*=bottom,
    axis y line*=left,
    tick label style={font=\small},
    grid=both,
    tick align=outside, 
    tickpos=left,
    xlabel= {[ACh]} (nM),
    ylabel=Response (mm),
   %%% GRAPH RANGE %%%
        xmin=0.1, xmax=1000,
        ymin=0, ymax=160,
   %%%%%%%%%%%%%%
        width=0.7\textwidth,
        height=0.4\textwidth,
    ]

\addplot[only marks, mark size=1.8, black] file {drc1.dat};
    % Now call gnuplot to fit this data
    % The key is the raw gnuplot option
    % which allows to write a gnuplot script file
    \addlegendentry[anchor=mid,font=\tiny]{Experiment 1}

\addplot+[raw gnuplot, red, mark=none, smooth] gnuplot {
    set log x;
    f(x)=Ymax/(1+(EC50/x)^nH);
    % let gnuplot fit, using column 1 and 2 of the data file
    % using the following initial guesses
    Ymax=150;
    nH=1;
    EC50=50;
    set fit errorvariables;
     fit f(x) 'drc1.dat' using 1:2 via Ymax,EC50,nH;
         % Next, plot the function and specify plot range
         % The range should be approx. the same as the test.dat x range
     plot [x=0.1:1000] f(x);
    set print "parameters.dat"; % Open a file to save the parameters into
    print Ymax, Ymax_err; % Write the parameters to file
    print nH, nH_err;
    print EC50, EC50_err;
       };       
\addlegendentry[font=\tiny, text depth=2ex]{\pgfplotstableread{parameters.dat}\parameters % Open the file Gnuplot wrote
    \pgfplotstablegetelem{0}{0}\of\parameters \pgfmathsetmacro\paramA{\pgfplotsretval} % Get first element, save into \paramA
    \pgfplotstablegetelem{1}{0}\of\parameters \pgfmathsetmacro\paramB{\pgfplotsretval}
    \pgfplotstablegetelem{2}{0}\of\parameters \pgfmathsetmacro\paramC{\pgfplotsretval}
     $\frac{\pgfmathprintnumber{\paramA}}{\left(1+\frac{\pgfmathprintnumber{\paramB}}{[ACh]}\right)^{\pgfmathprintnumber{\paramC}}}$    
}

\end{axis}
\end{tikzpicture}

\end{figure}

\pgfplotstabletypeset[
    dec sep align,
    fixed,
    columns/0/.style={
        column name=Parameter
    },
    columns/1/.style={
        column name=Standard Error
    }
]{parameters.dat}

\end{document}

Which will yield


And here's my earlier, much cruder approach:

I've defined a macro \extractcoefficients, which takes the number of coefficients as an argument, and then first counts the number of lines in fit.log, then parses the relevant lines near the end of the file, saves the coefficient names, values and standard errors into a pgfplotstable called \coefficients, and additionally stores the values in a pgfmath array called \coefficient.

You can then either output the whole table using \pgfplotstabletypeset [coefficient table] \coefficients};, or place the values of the coefficients where you need them using \coefficient{<number>}.

\documentclass[11pt]{article}
\usepackage{pgfplots}
\usepackage{filecontents}
\usepackage{pgfplotstable}

\begin{filecontents}{drc1.dat}
2   17
5   55
10  96
20  125
50  144
100 147
200 147
500 146
\end{filecontents}

\makeatletter
\newcommand\create[1]{%
\expandafter\newcommand\csname #1\endcsname{My name is #1}}
%% "Master" macro: Parse file and store table and coefficients
%  Number of coefficients as argument
\newcommand{\extractcoefficients}[1]{
    \linesinfile{fit.log} % Count number of lines
    \addtocounter{linecount}{-9} % Coefficients start 9 lines from end
    \edef\lastresultline{\arabic{linecount}}
    \addtocounter{linecount}{-#1}
    \stepcounter{linecount}
    \edef\firstresultline{\arabic{linecount}}
    \pgfplotstableread[ % Set up coefficient table
        header=has colnames,
        row sep=crcr
        ]{
        Parameter Value SE\\
    }\coefficients
    \partialinput{\firstresultline}{\lastresultline}{fit.log}
    \def\coefficientarray{2}
    \pgfplotstableforeachcolumnelement{Value}\of{\coefficients}\as{\value}{
        \edef\coefficientarray{\coefficientarray,\value}
    }
    \edef\coefficientarray{{\coefficientarray}}
}

%% Count number of lines in a file
\newread\lf
\newcounter{linecount}
\newcommand\linesinfile[1]{%
    \setcounter{linecount}{0}
    \openin\lf #1
    \unless\ifeof\lf
        \loop\unless\ifeof\lf
            \readline\lf to\lfline
            \stepcounter{linecount}
        \repeat
        \closein\lf
    \fi
}

%% Go through a file, execute a command for each line within specified range
\makeatletter
\newcommand*\partialinput [3] {%
  \IfFileExists{#3}{%
    \openin\lf #3
    \setcounter{linecount}{1}
    \@whilenum\value{linecount}<#1 \do{%
      \read\lf to\lfline
      \stepcounter{linecount}%
    }
    \addtocounter{linecount}{-1}
    \@whilenum\value{linecount}<#2 \do{%
      \readline\lf to\lfline
      \parsecoeff{\lfline}%
      \stepcounter{linecount}%
    }%
    \closein\lf%
  }{%
    \errmessage{File `#3' doesn't exist!}%
  }%
}
\makeatother

%% Store the whitespace-separated values of a line in a table
\newcommand\parsecoeff[1]{%
    \pgfplotstableset{alias/.cd,
            Parameter/.initial=0,
            Value/.initial=2,
            SE/.initial=4}
    \edef\read{\noexpand\pgfplotstableread[
        row sep=crcr,
        header=false,
        ]{#1\noexpand\\}\noexpand\test}
    \read%
    \pgfplotstablevertcat{\coefficients}{\test}
}

%% Return the formatted coefficient
\newcommand{\coefficient}[1]{%
    \pgfmathparse{\coefficientarray[#1]}%
    \pgfmathprintnumber{\pgfmathresult}
}

%% PGFplotstable style for printing the coefficient table
\tikzset{
    /pgfplots/table/coefficient table/.style={
        columns={Parameter,Value, SE},
        columns/Parameter/.style=string type,
        columns/Value/.style={dec sep align},
        columns/SE/.style={dec sep align, fixed, fixed zerofill, precision=3}
    }
}


\begin{document}

\begin{figure}[h!t]
\centering
\begin{tikzpicture}

\begin{axis}[
    legend pos=north west,
    xmode=log,
    ymode=linear,
    axis x line*=bottom,
    axis y line*=left,
    tick label style={font=\small},
    grid=both,
    tick align=outside, 
    tickpos=left,
    xlabel= {[ACh]} (nM),
    ylabel=Response (mm),
   %%% GRAPH RANGE %%%
        xmin=0.1, xmax=1000,
        ymin=0, ymax=180,
   %%%%%%%%%%%%%%
        width=0.7\textwidth,
        height=0.45\textwidth,
    ]

\addplot[only marks, mark size=1.8, color=blue] file {drc1.dat};
    % Now call gnuplot to fit this data
    % The key is the raw gnuplot option
    % which allows to write a gnuplot script file
    \addlegendentry{{\tiny Experiment 1}}

\addplot+[raw gnuplot, draw=blue, mark=none, smooth] gnuplot {
    set log x;
    f(x)=Ymax/(1+(EC50/x)^nH);
    % let gnuplot fit, using column 1 and 2 of the data file
    % using the following initial guesses
    Ymax=150;
    nH=1;
    EC50=50;
     fit f(x) 'drc1.dat' using 1:2 via Ymax,EC50,nH;
         % Next, plot the function and specify plot range
         % The range should be approx. the same as the test.dat x range
     plot [x=0.1:1000] f(x);
       };       

\extractcoefficients{3}
\addlegendentry[text depth=2ex]{%
    \small{%
        $\frac{\coefficient{1}}{\left(1+\frac{\coefficient{2}}{[ACh]}\right)^{\coefficient{3}}}$
    }
}
\node at (rel axis cs:0.975,0.2) [
    anchor=east,
    font=\tiny,
    fill=white,
    fill opacity=0.75,
    text opacity=1
    ] {
    \pgfplotstabletypeset [coefficient table] \coefficients};
\end{axis}
\end{tikzpicture}

\end{figure}
\end{document}