8

Three days ago I posted a question on Stack Exchange on how to fill the area between two curves (see question). It is my goal to have the fill area of two curves color differently based on when one is higher than the other. It is key that the results are consistent. This means that when plot 1 (target) is higher than plot 2 (benchmark), the area underneath should always be green. But when plot 2 is higher than plot 1, the area underneath should always be red. I received two great solutions from esdd and Ross for my question. However, both of these are not sufficient when the curves and background of the curves get more complicated.

Below you will find in the respective order:

  1. Screenshot of the plot that I'm working with
  2. MWE
  3. an explanation on why both solutions are good, but not optimal for this plot.

The solutions are included (and commented) in the MWE.

Screenshot: enter image description here

MWE:

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}
\pgfplotsset{compat=newest}
\pgfplotstableread[col sep=semicolon,trim cells]{
    index;value;bins;benchmark_hoeveelheid;benchmark_cumulatief;target_hoeveelheid;target_cumulatief;
    1;30;1-30;1;1;0;0;
    2;60;31-60;1;3;0;0;
    3;90;61-90;2;5;0;0;
    4;120;91-120;4;9;7;7;
    5;150;121-150;4;13;29;36;
    6;180;151-180;6;19;0;36;
    7;210;181-210;4;23;7;43;
    8;240;211-240;6;29;0;43;
    9;270;241-270;4;32;0;43;
    10;300;271-300;5;37;0;43;
    11;330;301-330;3;40;0;43;
    12;360;331-360;5;45;7;50;
    13;390;361-390;4;49;14;64;
    14;420;391-420;4;53;0;64;
    15;450;421-450;5;57;7;71;
    16;480;451-480;4;61;0;71;
    17;510;481-510;3;65;7;79;
    18;540;511-540;3;68;0;79;
    19;570;541-570;3;70;0;79;
    20;600;571-600;2;73;0;79;
    21;630;601-630;3;76;0;79;
    22;660;631-660;3;79;0;79;
    23;690;661-690;2;81;0;79;
    24;720;691-720;2;84;0;79;
    25;750;721-750;2;86;0;79;
    26;780;751-780;2;88;0;79;
    27;810;781-810;0;88;0;79;
    28;840;811-840;1;89;0;79;
    29;870;841-870;0;90;0;79;
    30;900;871-900;0;90;7;86;
    31;930;901-930;1;91;7;93;
    32;960;931-960;0;91;0;93;
    33;990;961-990;0;92;0;93;
    34;1020;991-1020;1;93;0;93;
    35;1050;1021-1050;1;93;0;93;
    36;1080;1051-1080;0;94;0;93;
    37;1110;1081-1110;0;94;7;100;
    38;1140;1111-1140;0;95;0;100;
}\data


\begin{document}
\begin{figure}[h]
    \begin{tikzpicture}[trim axis left]
    \begin{axis}[
    x tick label style={
        /pgf/number format/1000 sep=},
    scale only axis,
    height=10cm,
    width=\textwidth,
    ymin=0,
    every node near coord/.append style={font=\tiny, inner sep=1pt},
    xtick=data,
    xticklabels from table={\data}{value},
    %xticklabel={\euro\pgfmathprintnumber\tick},
    xticklabel style={rotate=45},
    xticklabel style={font=\tiny},
    yticklabel={\pgfmathparse{\tick*1}\pgfmathprintnumber{\pgfmathresult}\%},
    yticklabel style={font=\scriptsize},
    ymajorgrids,
    xmajorgrids,
    bar width=0.1cm,
    enlarge x limits=0.01,
    enlarge y limits={value=0.05,upper},
    legend style={at={(0.5,-0.07)},
        anchor=north,legend columns=-1},
    ]

    \addplot[name path=plot1,draw=green!60!black,thick,sharp plot]
    table [x=index, y=target_cumulatief] {\data};
    \addplot[name path=plot2,draw=blue!70!pink,thick,sharp plot]
    table [x=index, y=benchmark_cumulatief] {\data};

%   SOLUTION 1
%   
%     \path[name path=xaxis](current axis.south west)--(current axis.south east);
%     \addplot[fill opacity=0.2, green] fill between[ 
%     of = plot1 and plot2,
%     split
%     ];
%     \addplot[fill opacity=1, white] fill between[of = plot1 and xaxis];

%   SOLUTION 2
%     \addplot
%     fill between[of = plot1 and plot2,
%     split,
%     every segment no 0/.style={fill=green, fill opacity=0.2},
%     every segment no 1/.style={fill=red, fill opacity=0.2},  
%     every segment no 2/.style={fill=green, fill opacity=0.2},
%     every segment no 3/.style={fill=red, fill opacity=0.2},
%     every segment no 4/.style={fill=green, fill opacity=0.2},
%     every segment no 5/.style={fill=red, fill opacity=0.2},
%     every segment no 6/.style={fill=green, fill opacity=0.2},
%     every segment no 7/.style={fill=red, fill opacity=0.2},
%     ];
    \legend{{Target},{Benchmark}}
    \end{axis}
    \end{tikzpicture}
\end{figure}
\end{document}

Solution 1:

enter image description here

The first solution uses the difference between the x-axis and the two curves to colour the area underneath. This means that while it is possible to color the area under the curve when plot 1 is higher than plot 2, vice versa it doesn't work. Besides, the grid gets blocked by the white.

Solution 2:

enter image description here

This solution is getting closer to what I want, but it lacks consistency. You have to specify what color you want the different segments to have, which means that it wouldn't listen automatically to my predefined rules of 'plot 1>plot2 = green' and 'plot2>plot1 = red'.

I know I might sound very picky, but this is very important for me to get it 100% right. These examples would just not be sufficient if I were to plot a large quantity of these type of graphs, since I would have to manually tweak all of them.

Hopefully someone can help me based on these details. Also, I would like to mention this question+solution, since I think that the findintersections command created in here could be the winning solution. However, it would not work in this case since you need a constant and a curve, while I'm working with two curves. Nonetheless, if it could be tweaked it would work.

  • 1
    You have every even segment and every odd segment. And you could create a new key like firstgreen/.style={every even segment/.style{fill=green, fill opacity=0.2},every odd segment/.style{fill=red, fill opacity=0.2},} and another firstred. And then use just that keyword in each graph. You have to input it manually, but it's simpler. – Manuel Jul 12 '17 at 10:41
  • Hi Manual, A solution with even and odd won't work. As an explanation of why I would like to point to solution 2 of my questions above. In there I also label even and odd segments interchangeably, which is similar to your suggestion. As you can see at x=1080, it takes the wrong color for coloring the segment. That is probably because the graph got confused when it was overlapping at x=990. Since I can't afford to risk one of my graphs having such an error when I produce them in bulk, I need a better solution then checking them manually. – Raphael Spaans Jul 12 '17 at 11:25
  • 1
    Possible duplicate of Fill area between two curves – Uwe Keim Jul 12 '17 at 13:24
  • The way "solution 2" is implemented is not correct. Segment 4 corresponds to the x-axis value of 1020, where the y-axis values for target and benchmark are both 93. You can give segment 4 any fill color because there is nothing to fill. The correct usage would be:every segment no 4/.style={fill=<anything>},every segment no 5/.style={fill=green,opacity=0.2} etc, whereas you had used red for segment 5. @esdd provides a much better solution for the problem as posted, but this comment clarifies that the every segment syntax does not lead to an inconsistent result. – Ross Jul 18 '17 at 9:05
7

You can fill the areas in layer axis background. Then their filling is behind the grid.

Maybe its possible to use slightly different colors for the filling. Then you can fill the are between the curves with a color and then fill the area between one of the curves and the x axis white. In the last step you can color the area between the curves with a second color using an opacity.

enter image description here

Code:

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}
\pgfplotsset{compat=newest}
\pgfplotstableread[col sep=semicolon,trim cells]{
    index;value;bins;benchmark_hoeveelheid;benchmark_cumulatief;target_hoeveelheid;target_cumulatief;
    1;30;1-30;1;1;0;0;
    2;60;31-60;1;3;0;0;
    3;90;61-90;2;5;0;0;
    4;120;91-120;4;9;7;7;
    5;150;121-150;4;13;29;36;
    6;180;151-180;6;19;0;36;
    7;210;181-210;4;23;7;43;
    8;240;211-240;6;29;0;43;
    9;270;241-270;4;32;0;43;
    10;300;271-300;5;37;0;43;
    11;330;301-330;3;40;0;43;
    12;360;331-360;5;45;7;50;
    13;390;361-390;4;49;14;64;
    14;420;391-420;4;53;0;64;
    15;450;421-450;5;57;7;71;
    16;480;451-480;4;61;0;71;
    17;510;481-510;3;65;7;79;
    18;540;511-540;3;68;0;79;
    19;570;541-570;3;70;0;79;
    20;600;571-600;2;73;0;79;
    21;630;601-630;3;76;0;79;
    22;660;631-660;3;79;0;79;
    23;690;661-690;2;81;0;79;
    24;720;691-720;2;84;0;79;
    25;750;721-750;2;86;0;79;
    26;780;751-780;2;88;0;79;
    27;810;781-810;0;88;0;79;
    28;840;811-840;1;89;0;79;
    29;870;841-870;0;90;0;79;
    30;900;871-900;0;90;7;86;
    31;930;901-930;1;91;7;93;
    32;960;931-960;0;91;0;93;
    33;990;961-990;0;92;0;93;
    34;1020;991-1020;1;93;0;93;
    35;1050;1021-1050;1;93;0;93;
    36;1080;1051-1080;0;94;0;93;
    37;1110;1081-1110;0;94;7;100;
    38;1140;1111-1140;0;95;0;100;
}\data


\begin{document}
\begin{figure}[h]
    \begin{tikzpicture}[trim axis left,
    fill between/on layer=axis background% <- filling behind the grid
    ]
    \begin{axis}[
    x tick label style={
        /pgf/number format/1000 sep=},
    scale only axis,
    height=10cm,
    width=\textwidth,
    ymin=0,
    every node near coord/.append style={font=\tiny, inner sep=1pt},
    xtick=data,
    xticklabels from table={\data}{value},
    %xticklabel={\euro\pgfmathprintnumber\tick},
    xticklabel style={rotate=45},
    xticklabel style={font=\tiny},
    yticklabel={\pgfmathparse{\tick*1}\pgfmathprintnumber{\pgfmathresult}\%},
    yticklabel style={font=\scriptsize},
    ymajorgrids,
    xmajorgrids,
    bar width=0.1cm,
    enlarge x limits=0.01,
    enlarge y limits={value=0.05,upper},
    legend style={at={(0.5,-0.07)},
        anchor=north,legend columns=-1},
    ]

    \addplot[name path=plot1,draw=green!60!black,thick,sharp plot]
    table [x=index, y=target_cumulatief] {\data};
    \addplot[name path=plot2,draw=blue!70!pink,thick,sharp plot]
    table [x=index, y=benchmark_cumulatief] {\data};

       \path[name path=xaxis](current axis.south west)--(current axis.south east);
       \addplot[green!30] fill between[ 
       of = plot1 and plot2,
       split
       ];
       \addplot[white] fill between[of = plot1 and xaxis];
       \addplot[red!70!yellow,fill opacity=.2] fill between[ 
       of = plot1 and plot2,
       split
       ];

    \legend{{Target},{Benchmark}}
    \end{axis}
    \end{tikzpicture}
\end{figure}
\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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