3

I'm trying to use \groupplots and \pgfplotsinvokeforeach to generate a dynamic picture showing different profiles. Within those profiles I'd like to highlight different ranges (sequences) with a specific background color.

The sequences are in a separate *.csv file with it's corresponding start/end time and color code. I couldn't get those files loaded, reading each single row and highlight the corresponding range. Nevertheless, most of my code seems to work, since I get a result which is quite close but the inner loop does not seem to work correctly.

\documentclass[tikz]{standalone}
\usepackage{filecontents}

\usepackage{siunitx} % added this line because of marmot's comment
\usepackage{pgfplots}
\usepackage{tikz}

\RequirePackage{pgfplotstable} % Allows to read tables

\pgfplotsset{compat=newest} % Ensure compatibility mode

\usepgfplotslibrary{groupplots} % Groupplots (multiple x-Axis together)
\usetikzlibrary{backgrounds} % Background Library
\usetikzlibrary{calc} % Allows extended coordinate calculations

\newcommand{\tikzsetnextfilename}[1]{\ignorespaces}

\begin{document}
    % externalise TikZ (when not in standalone) - use same filename
    \tikzsetnextfilename{\currfilebase}

    % Data table
    \pgfplotstableset{
        col sep={semicolon}
    }

    \begin{filecontents}{Profile1.csv}
        t_h;I_A;U_V
        0;0;3.584
        0.06905;-0.67;3.53
        0.1381;-0.67;3.514
        0.2072;-0.67;3.5
        0.2762;-0.67;3.484
        0.3453;-0.67;3.466
        0.4143;-0.67;3.448
        0.4834;-0.67;3.428
        0.5524;-0.67;3.412
        0.6215;-0.67;3.399
        0.6905;-0.67;3.385
        0.7596;-0.67;3.369
        0.8287;-0.67;3.35
        0.8977;-0.67;3.321
        0.9668;-0.67;3.275
        1.036;-0.67;3.231
    \end{filecontents}

    \begin{filecontents}{Sequences1.csv}
        tstart_h;tend_h;type
        0.0;0.2762;1
        0.3453;0.6215;2
        0.6905;0.8977;3
        0.9668;1.036;1
    \end{filecontents}

    \begin{filecontents}{Profile2.csv}
        t_h;I_A;U_V
        0;0;3.705
        0.07158;0;3.705
        0.1432;0;3.705
        0.2148;1.675;3.851
        0.2863;1.675;3.892
        0.3579;1.675;3.93
        0.4295;1.675;3.966
        0.5011;1.675;4.001
        0.5727;1.675;4.035
        0.6443;1.675;4.071
        0.7158;1.675;4.11
        0.7874;1.675;4.154
        0.859;1.675;4.196
        0.9306;1.33;4.2
        1.002;1.136;4.2
        1.074;0.9927;4.2
    \end{filecontents}

    \begin{filecontents}{Sequences2.csv}
        tstart_h;tend_h;type
        0.0;0.2148;2
        0.2863;0.5011;1
        0.5727;0.859;2
        0.9306;1.074;3
    \end{filecontents}

    \begin{tikzpicture}

        \pgfplotsset{
            plot coordinates/math parser=true,
            unbounded coords=jump,
            filter discard warning=false,
        %% Colormap
            colormap={sequencecolors}{
                rgb=(0.75,0,0)
                rgb=(0,0.75,0)
                rgb=(0,0,0.75)
            },
        %% Axis
            scale only axis,
            width=0.8\linewidth,
            height=2cm,
            every axis/.append style={
                line width=1pt,
                tick style={line width=0.8pt},
                grid style={dashed, black!20},
                grid=major,
            },
        %% X-Axis
            xmin=-0.1,
            xmax=1.2,
        }

    %% Primary Y-Axis
        \begin{groupplot}[
            group style={
                group size=1 by 2,
                xlabels at=edge bottom,
                xticklabels at=edge bottom,
                vertical sep=2mm
            },
            tickpos=left,
        %% X-Axis
            xlabel={$t$ in \si{\hour}},
        %% Y-Axis
            ymin=2.4,
            ymax=4.3,
        ]

            \pgfplotsinvokeforeach{1,...,2}{
                % Each Profile in seperate groupplot
                \nextgroupplot [ylabel=Seq #1] {
                    % Profile
                    \addplot [color=blue, forget plot] table [x=t_h, y=U_V] {Profile#1.csv};

                    % Seqence
                    \pgfplotstableread{Sequences#1.csv}{\sequences}
                    \pgfplotstablegetrowsof{\sequences}
                    \pgfmathsetmacro{\rows}{\pgfplotsretval-1}

                    \begin{scope} [on background layer]
                        \pgfplotsinvokeforeach{1,...,\rows}{
                            % Get row from sequences
                            \pgfplotstablegetelem{#1}{tstart_h}\of{\sequences}\edef\tailTime{\pgfplotsretval}
                            \pgfplotstablegetelem{#1}{tend_h}\of{\sequences}\edef\headTime{\pgfplotsretval}
                            \pgfplotstablegetelem{#1}{type}\of{\sequences}\edef\typeColor{\pgfplotsretval}

                            %% This is where the magic should happen, second line is only for debugging, first line does not work properly but what's wrong?
                            % \fill [color of colormap={\typeColor}, opacity=0.1] (\tailTime , \pgfkeysvalueof{/pgfplots/ymin}) rectangle (\headTime, \pgfkeysvalueof{/pgfplots/ymax});
                            \fill [color of colormap={#1}, opacity=0.1] ( {#1*0.1}, \pgfkeysvalueof{/pgfplots/ymin}) rectangle ( {(#1+1)*0.1}, \pgfkeysvalueof{/pgfplots/ymax});
                        }
                    \end{scope}
                }
            }
        \end{groupplot}

    %% Secondary Y-Axis
        \begin{groupplot}[
            group style={
                group size=1 by 2,
                xlabels at=edge bottom,
                xticklabels at=edge bottom,
                vertical sep=2mm,
                y descriptions at=edge right
            },
            grid=none,
            tickpos=right,
        %% X-Axis
            xticklabels={,,},
        %% Y-Axis
            ymin=-6,
            ymax=6,
        ]

        \pgfplotsinvokeforeach{1,...,2}{
            % Each Profile in seperate groupplot
            \nextgroupplot [ylabel=Seq #1] {
                % Profile
                \addplot [color=red, forget plot] table [x=t_h, y=I_A] {Profile#1.csv};
            }
        }
        \end{groupplot}
    \end{tikzpicture}
\end{document}

Results in:

Result of the provided MWE

With the additional \fill command, I get several error message like:

filename.tex:149: Package PGF Math Error: Sorry, an internal routine of the loating point unit got an ill-formatted floating point number `130.0'. The unreadable part was near '130.0'..
filename.tex:149: Illegal unit of measure (pt inserted)

Doing everything by hand is not possible, the provided data is only a very tiny excerpt of the whole dataset. Is there a way to handle this or do I have to use a seperate column in the profile data which names the color code to handle this any other way?

MFH
  • 33
  • 4
  • If you replace \si{\hour} by \hour, it works. –  Jan 06 '18 at 16:43
  • @marmot Thank you for the hint. But this solves not the main problem with the inner loop. Normally I have siunitx package loaded – MFH Jan 06 '18 at 16:56

1 Answers1

2

There were two issues. First, the #1 in the inner loop unfortunately got still "remembered" the #1 from the outer loop. So I fixed that by hand. Second, and more importantly, pgfplots seems to have the "feature" of postponing expansions, and it expanded the \fill command when it already had forgotten the values you read in the inner loop. So I'd propose this code.

\documentclass[tikz]{standalone}
\usepackage{filecontents}

\usepackage{siunitx} % added this line because of marmot's comment
\usepackage{pgfplots}
\usepackage{tikz}

\RequirePackage{pgfplotstable} % Allows to read tables

\pgfplotsset{compat=newest} % Ensure compatibility mode

\usepgfplotslibrary{groupplots} % Groupplots (multiple x-Axis together)
\usetikzlibrary{backgrounds} % Background Library
\usetikzlibrary{calc} % Allows extended coordinate calculations
\newcounter{irun}

\newcommand{\tikzsetnextfilename}[1]{\ignorespaces}

\begin{document}
    % externalise TikZ (when not in standalone) - use same filename
    \tikzsetnextfilename{\currfilebase}
    % Data table
    \pgfplotstableset{
        col sep={semicolon}
    }

    \begin{filecontents}{Profile1.csv}
        t_h;I_A;U_V
        0;0;3.584
        0.06905;-0.67;3.53
        0.1381;-0.67;3.514
        0.2072;-0.67;3.5
        0.2762;-0.67;3.484
        0.3453;-0.67;3.466
        0.4143;-0.67;3.448
        0.4834;-0.67;3.428
        0.5524;-0.67;3.412
        0.6215;-0.67;3.399
        0.6905;-0.67;3.385
        0.7596;-0.67;3.369
        0.8287;-0.67;3.35
        0.8977;-0.67;3.321
        0.9668;-0.67;3.275
        1.036;-0.67;3.231
    \end{filecontents}

    \begin{filecontents}{Sequences1.csv}
        tstart_h;tend_h;type
        0.0;0.2762;1
        0.3453;0.6215;2
        0.6905;0.8977;3
        0.9668;1.036;1
    \end{filecontents}

    \begin{filecontents}{Profile2.csv}
        t_h;I_A;U_V
        0;0;3.705
        0.07158;0;3.705
        0.1432;0;3.705
        0.2148;1.675;3.851
        0.2863;1.675;3.892
        0.3579;1.675;3.93
        0.4295;1.675;3.966
        0.5011;1.675;4.001
        0.5727;1.675;4.035
        0.6443;1.675;4.071
        0.7158;1.675;4.11
        0.7874;1.675;4.154
        0.859;1.675;4.196
        0.9306;1.33;4.2
        1.002;1.136;4.2
        1.074;0.9927;4.2
    \end{filecontents}

    \begin{filecontents}{Sequences2.csv}
        tstart_h;tend_h;type
        0.0;0.2148;2
        0.2863;0.5011;1
        0.5727;0.859;2
        0.9306;1.074;3
    \end{filecontents}

    \begin{tikzpicture}

        \pgfplotsset{
            plot coordinates/math parser=true,
            unbounded coords=jump,
            filter discard warning=false,
        %% Colormap
            colormap={sequencecolors}{
                rgb=(0.75,0,0)
                rgb=(0,0.75,0)
                rgb=(0,0,0.75)
            },
        %% Axis
            scale only axis,
            width=0.8\linewidth,
            height=2cm,
            every axis/.append style={
                line width=1pt,
                tick style={line width=0.8pt},
                grid style={dashed, black!20},
                grid=major,
            },
        %% X-Axis
            xmin=-0.1,
            xmax=1.2,
        }

    %% Primary Y-Axis
        \begin{groupplot}[
            group style={
                group size=1 by 2,
                xlabels at=edge bottom,
                xticklabels at=edge bottom,
                vertical sep=2mm
            },
            tickpos=left,
        %% X-Axis
            xlabel={$t$ in \si{\hour}},
        %% Y-Axis
            ymin=2.4,
            ymax=4.3,
        ]

            \pgfplotsinvokeforeach{1,...,2}{\setcounter{irun}{0}
                % Each Profile in seperate groupplot
                \nextgroupplot [ylabel=Seq #1] {
                    % Profile
                    \addplot [color=blue, forget plot] table [x=t_h, y=U_V] {Profile#1.csv};

                    % Seqence
                    \pgfplotstableread{Sequences#1.csv}{\sequences}
                    \pgfplotstablegetrowsof{\sequences}
                    \pgfmathtruncatemacro{\rows}{\pgfplotsretval}
                    \typeout{\rows\space rows}

                    \begin{scope} [on background layer]
                        \pgfplotsinvokeforeach{1,...,\rows}{
                            % Get row from sequences
                            \pgfplotstablegetelem{\theirun}{tstart_h}\of{\sequences}\edef\tailTime{\pgfplotsretval}
                            \pgfplotstablegetelem{\theirun}{tend_h}\of{\sequences}\edef\headTime{\pgfplotsretval}
                            \pgfplotstablegetelem{\theirun}{type}\of{\sequences}\edef\typeColor{\pgfplotsretval}
                            \stepcounter{irun}
                            \typeout{run:\space #1,\space subrun:\space\theirun/\rows:\space\typeColor\space\tailTime\space\headTime}

                            %% This is where the magic should happen, second line is only for debugging, first line does not work properly but what's wrong?
                            \edef\temp{\noexpand\fill [color of
                            colormap=14*\typeColor cm, opacity=0.1] (\tailTime ,
                            \pgfkeysvalueof{/pgfplots/ymin}) rectangle (\headTime,
                            \pgfkeysvalueof{/pgfplots/ymax});}
                            \temp %from https://tex.stackexchange.com/a/170670/121799
                            %\fill [color of colormap={#1}, opacity=0.1] ( {#1*0.1}, \pgfkeysvalueof{/pgfplots/ymin}) rectangle ( {(#1+1)*0.1}, \pgfkeysvalueof{/pgfplots/ymax});
                        }
                    \end{scope}
                }
            }
        \end{groupplot}

    %% Secondary Y-Axis
        \begin{groupplot}[
            group style={
                group size=1 by 2,
                xlabels at=edge bottom,
                xticklabels at=edge bottom,
                vertical sep=2mm,
                y descriptions at=edge right
            },
            grid=none,
            tickpos=right,
        %% X-Axis
            xticklabels={,,},
        %% Y-Axis
            ymin=-6,
            ymax=6,
        ]

        \pgfplotsinvokeforeach{1,...,2}{
            % Each Profile in seperate groupplot
            \nextgroupplot [ylabel=Seq #1] {
                % Profile
                \addplot [color=red, forget plot] table [x=t_h, y=I_A] {Profile#1.csv};
            }
        }
        \end{groupplot}
    \end{tikzpicture}
\end{document}

EDIT: I modified the color command just to double-check that the shading is indeed read. With the original setting, the colors were indistinguishable for ordinary marmot eyes.

enter image description here

  • Works like a charm! Thanks a lot. Didn't know the \typeout command, this is quiet useful for debugging (y) – MFH Jan 07 '18 at 13:56