PGFPlots — заполнение области между двумя кривыми

PGFPlots — заполнение области между двумя кривыми

Недавно я нашел отличный пример, предоставленный сообществом Stack Exchange, который fillbetweenиспользовался для заполнения области междукриваяиконстанта(см. 1-е изображение). Мне это особенно понравилось, так как цвет области меняется в зависимости от того, больше или меньше кривая константы. Это стало возможным благодаря функции, findintersectionsпредоставленной Джейком (ссылка на решение). Однако я думаю, что было бы особенно полезно, если бы он заполнил область междудве кривые(см. 2-е изображение). Я предоставил 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}

Изображение 1

Изображение 2

решение1

Начиная с pgfplotsверсии 1.10 вы можете загрузить библиотеку 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}

результаты в

введите описание изображения здесь


Если axis backgroundможно залить белым и вы хотите окрасить в желтый цвет только те области, где красная кривая выше синей, вы можете использовать

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

результаты (я изменил некоторые значения в файлах данных)

введите описание изображения здесь

К сожалению, я не нашел решения, при котором области с более высокой красной кривой можно было бы закрасить желтым цветом, а области с более низкой красной кривой — серым на одной и той же картинке.

решение2

Вы можете использовать every segment no <index>синтаксис здесь, чтобы указать форматирование каждого отдельного сегмента заполнения. См. раздел 4.5.10 и 5.7.2 - 5.7.4 руководства PgfPlots.

Другой вариант — создать два ряда данных для путей, которые определяют верхнюю и нижнюю границы разницы между двумя наборами данных. Их
можно использовать в качестве помощников для определения области заполнения, так же как ось x используется в качестве помощника в ответе esdd, но помощник оси x здесь не требуется.

Вот что получилось: введите описание изображения здесь

Это МВЭ:

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

Связанный контент