PGFPlots - Preencher área entre duas curvas

PGFPlots - Preencher área entre duas curvas

Recentemente encontrei um ótimo exemplo fornecido pela comunidade Stack Exchange onde fillbetweenfoi usado para preencher a área entreuma curvaeuma constante(ver 1ª imagem). Gostei principalmente porque a cor da área muda dependendo se a curva é maior ou menor que a constante. Isso foi possível graças a uma findintersectionsfunção fornecida por Jake (link para solução). No entanto, penso que seria especialmente útil se preenchesse uma área entreduas curvas(ver 2ª imagem). Forneci um MWE abaixo, que possui duas curvas e uma constante. Alguém poderia fornecer uma solução onde as duas curvas sejam comparadas entre si. Então com base em qual é maior que o outro, mude a cor entre as curvas? Acho que fazer isso seria muito útil para gráficos que, de outra forma, seriam difíceis de interpretar, onde as diferenças entre duas curvas são pequenas e há muita sobreposição.

MWE:

\documentclass{article}

\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{filecontents}
\usetikzlibrary{calc}
\begin{filecontents}{data.dat}
0 0.2
1 0.217
2 0.255
3 0.288
6 0.58
7 0.91
8 1.02
10 1.05
12 0.92
13 0.78
15 0.56
17 1.1
\end{filecontents}
\begin{filecontents}{data2.dat}
    0 1.2
    1 0.8
    2 0.6
    3 0.20
    6 0.4
    7 0.5
    8 1.5
    10 1.5
    12 0.5
    13 0.9
    15 0.4
    17 1.1
\end{filecontents}

\pgfplotstableread{data.dat}\data
\pgfplotstableread{data2.dat}\datas

\newcommand\findintersections[2]{
    \def\prevcell{#1}
    \pgfplotstableforeachcolumnelement{1}\of#2\as\cell{%
        \pgfmathparse{!or(
            and(
                \prevcell>#1,\cell>#1
            ),
            and(
                \prevcell<#1,\cell<#1
            )
        )}

        \ifnum\pgfmathresult=1
            \pgfplotstablegetelem{\pgfplotstablerow}{0}\of{\data} \let\xb=\pgfplotsretval
            \pgfplotstablegetelem{\pgfplotstablerow}{1}\of{\data} \let\yb=\pgfplotsretval
            \pgfmathtruncatemacro\previousrow{ifthenelse(\pgfplotstablerow>0,\pgfplotstablerow-1,0)}
            \pgfplotstablegetelem{\previousrow}{0}\of{\data} \let\xa=\pgfplotsretval
            \pgfplotstablegetelem{\previousrow}{1}\of{\data} \let\ya=\pgfplotsretval
            \pgfmathsetmacro\newx{
                \xa+(\ya-#1)/(ifthenelse(\yb==\ya,1,\ya-\yb) )*(\xb-\xa)    }

            \edef\test{\noexpand\pgfplotstableread[col sep=comma,row sep=crcr,header=has colnames]{
                0,1\noexpand\\
                \newx,#1\noexpand\\
            }\noexpand\newrow}
            \test
            \pgfplotstablevertcat\interpolated{\newrow}
        \fi
        \let\prevcell=\cell
    }
    \pgfplotstablevertcat\interpolated{#2}
    \pgfplotstablesort[sort cmp={float <}]\interpolated{\interpolated}
    \pgfplotstableset{
        create on use/above line/.style={
            create col/expr={max(\thisrow{1},#1)}
        },
        create on use/below line/.style={
            create col/expr={min(\thisrow{1},#1)}
        },
    }
}


\begin{document}
\pgfplotsset{compat=newest} % For nicer label placement

\findintersections{0.9}{\data}

\begin{tikzpicture}
\begin{axis}[
    xlabel=Time of day,
    ylabel=Volume,
    ytick=\empty,
    axis x line=bottom,
    axis y line=left,
    enlargelimits=true
    ]
\addplot[fill,gray!20!white,no markers,line width=2pt] table [y=above line] {\interpolated} |- (current plot begin);
\addplot[fill,yellow!20!white,no markers,line width=2pt] table [y=below line] {\interpolated} |- (current plot begin);
\addplot[orange,no markers,line width=2pt,domain=-1:20] {0.9};
\addplot[blue,line width=2pt,mark=*] table  {\data};
\addplot[red,line width=2pt,mark=*] table  {\datas};
\end{axis}
\end{tikzpicture}

\end{document}

Imagem 1

Imagem 2

Responder1

Desde pgfplotsa versão 1.10 você pode carregar a biblioteca fillbetween:

\documentclass{article}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.15}% current version is 1.15
\usepgfplotslibrary{fillbetween}
\usepackage{filecontents}
\begin{filecontents}{data.dat}
0 0.2
1 0.217
2 0.255
3 0.288
6 0.58
7 0.91
8 1.02
10 1.05
12 0.92
13 0.78
15 0.56
17 1.1
\end{filecontents}
\begin{filecontents}{data2.dat}
0 1.2
1 0.8
2 0.6
3 0.20
6 0.4
7 0.5
8 1.5
10 1.5
12 0.5
13 0.9
15 0.4
17 1.1
\end{filecontents}

\pgfplotstableread{data.dat}\data
\pgfplotstableread{data2.dat}\datas

\begin{document}
\begin{tikzpicture}
  \begin{axis}[
      xlabel=Time of day,
      ylabel=Volume,
      ytick=\empty,
      axis x line=bottom,
      axis y line=left,
      enlargelimits=true
    ]
  \addplot[name path=plot1,blue,line width=2pt,mark=*] table {\data};
  \addplot[name path=plot2,red,line width=2pt,mark=*]  table {\datas};
  \addplot fill between[ 
    of = plot1 and plot2, 
    split, % calculate segments
    every even segment/.style = {yellow!20!white},
    every odd segment/.style ={gray!20!white}
  ]; 
  \end{axis}
\end{tikzpicture}
\end{document}

resulta em

insira a descrição da imagem aqui


Se axis backgroundpuder ser preenchido com branco e você quiser apenas colorir de amarelo as áreas onde a curva vermelha é mais alta que a azul, você pode usar

\documentclass{article}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.15}% current version is 1.15
\usepgfplotslibrary{fillbetween}
\usepackage{filecontents}
\begin{filecontents}{data.dat}
0 0.2
1 0.217
2 0.255
3 0.288
6 0.58
7 0.91
8 1.02
10 1.05
12 0.92
13 0.78
15 0.56
17 1.1
\end{filecontents}
\begin{filecontents}{data2.dat}
0 1.2
1 0.8
2 0.6
3 0.288
4 1
6 0.4
7 0.5
8 1.5
10 1.5
12 0.5
13 0.9
15 0.4
17 1.1
\end{filecontents}

\pgfplotstableread{data.dat}\data
\pgfplotstableread{data2.dat}\datas

\begin{document}
\begin{tikzpicture}
  \begin{axis}[
      xlabel=Time of day,
      ylabel=Volume,
      ytick=\empty,
      axis x line=bottom,
      axis y line=left,
      enlargelimits=true,
      axis background/.style={fill=white}
    ]
  \addplot[name path=plot1,blue,line width=2pt,mark=*] table {\data};
  \addplot[name path=plot2,red,line width=2pt,mark=*]  table {\datas};
  \path[name path=xaxis](current axis.south west)--(current axis.south east);
  \addplot[yellow!20!white] fill between[ 
    of = plot1 and plot2,
    split
  ];
  \addplot[axis background] fill between[of = plot1 and xaxis];
  \end{axis}
\end{tikzpicture}
\end{document}

resulta em (alterei alguns valores nos arquivos de dados)

insira a descrição da imagem aqui

Infelizmente não encontrei uma solução onde as áreas com curva vermelha mais alta possam ser coloridas de amarelo e aquelas com curva vermelha mais baixa possam ser coloridas de cinza na mesma imagem.

Responder2

Você pode usar a every segment no <index>sintaxe aqui para especificar a formatação de cada segmento de preenchimento individual. Consulte as seções 4.5.10 e 5.7.2 - 5.7.4 do PgfPlotsmanual.

A outra opção é criar duas séries de dados para os caminhos que definem os limites superior e inferior da diferença entre os dois conjuntos de dados. Eles
poderiam ser usados ​​como auxiliares para definir a área de preenchimento, assim como o eixo x está sendo usado como auxiliar na resposta do esdd, mas o auxiliar do eixo x não é necessário aqui.

Esta é a saída: insira a descrição da imagem aqui

Este é o MWE:

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}
\pgfplotsset{compat=newest} % For nicer label
\usepackage{pgfplotstable}
\usepackage{filecontents}
\usepackage{xcolor}
\colorlet{higher}{yellow!30}
\colorlet{lower}{lightgray}

\begin{filecontents}{data.dat}
0 0.2
1 0.217
2 0.255
3 0.288
6 0.58
7 0.91
8 1.02
10 1.05
12 0.92
13 0.78
15 0.56
17 1.1
\end{filecontents}
\begin{filecontents}{data2.dat}
    0 1.2
    1 0.8
    2 0.6
    3 0.20
    6 0.4
    7 0.5
    8 1.5
    10 1.5
    12 0.5
    13 0.9
    15 0.4
    17 1.1
\end{filecontents}

\pgfplotstableread{data.dat}\data
\pgfplotstableread{data2.dat}\datas

\begin{document}

\begin{tikzpicture}
\begin{axis}[
    xlabel=Time of day,
    ylabel=Volume,
    ytick=\empty,
    axis x line=bottom,
    axis y line=left,
    enlargelimits=true
    ]
\addplot[name path=plot1,blue,line width=2pt,mark=*] table  {\data};
\addplot[name path=plot2,red,line width=2pt,mark=*] table  {\datas};
  \addplot
  fill between[of = plot1 and plot2,
  split,
  every segment no 0/.style={fill=higher},
  every segment no 1/.style={fill=lower},  
  every segment no 2/.style={fill=higher},
  every segment no 3/.style={fill=lower},
  every segment no 4/.style={fill=higher},
  every segment no 5/.style={fill=lower},
  ]
  ;
\end{axis}
\end{tikzpicture}

\end{document}

informação relacionada