Seite 1 von 2

pgfplots: Fläche zwischen zwei Funktionen einfärben

Verfasst: Di 12. Nov 2013, 22:58
von Mac-Cherony
Hallo,

ich möchte mit pgfplots die Fläche zwischen zwei Funktionen einfärben und zwar je nachdem ob y1>y2 oder umgekehrt ist. Also z.B. eine rote Füllung wenn y1<y2 ist und eine grüne wenn y1>y2 ist.

Die Daten habe ich als Textdatei vorliegen. Ich habe ein lauffähiges Minimalbeispiel erarbeitet. Bisher ist es mir leider nur gelungen die Fläche zwischen den Plots mit nur einer Farbe zu füllen. Kann mir hier Jemand weiterhelfen?
\documentclass{standalone}
\usepackage{pgfplots}
\usepackage{filecontents}

\begin{filecontents}{plotdata.dat}
-3.5 0.350783228 -0.343633445
-3.25 0.108195135 -0.107984166
-3 -0.141120008 0.140652077
-2.75 -0.381660992 0.372462462
-2.5 -0.598472144 0.563380821
-2.25 -0.778073197 0.701908324
-2 -0.909297427 0.789072344
-1.75 -0.983985947 0.83271103
-1.5 -0.997494987 0.840114882
-1.25 -0.948984619 0.812824456
-1 -0.841470985 0.745624142
-0.75 -0.68163876 0.630066434
-0.5 -0.479425539 0.461269555
-0.25 -0.247403959 0.244887792
0 0 0
0.25 0.247403959 -0.244887792
0.5 0.479425539 -0.461269555
0.75 0.68163876 -0.630066434
1 0.841470985 -0.745624142
1.25 0.948984619 -0.812824456
1.5 0.997494987 -0.840114882
1.75 0.983985947 -0.83271103
2 0.909297427 -0.789072344
2.25 0.778073197 -0.701908324
2.5 0.598472144 -0.563380821
2.75 0.381660992 -0.372462462
3 0.141120008 -0.140652077
3.25 -0.108195135 0.107984166
3.5 -0.350783228 0.343633445
\end{filecontents}


\begin{document}
\begin{tikzpicture}
\begin{axis} []
 \addplot [] table[x index={0}, y index={1}] {plotdata.dat};
 \addplot [] table[x index={0}, y index={2}] {plotdata.dat};
 
\addplot [draw=none,stack plots=y,forget plot] table [y index={1}] {plotdata.dat};
\addplot [draw=none, fill=gray, opacity=.5,stack plots=y] table [y expr=\thisrowno{2}-\thisrowno{1}] {plotdata.dat}\closedcycle;

\end{axis}
\end{tikzpicture}
\end{document}
Danke schonmal für die Hilfe!

Verfasst: Mi 13. Nov 2013, 15:30
von esdd
Schönes Minimalbeispiel, aber das Problem selbst ist schwierig. Ich glaub nicht, dass man das wirklich automatisch erreichen kann.

Hier ist mal ein Vorschlag, wie man das vielleicht machen kann:
\documentclass{standalone} 
\usepackage{pgfplots} 
\pgfplotsset{compat=1.9}
\usetikzlibrary{intersections}
\usepackage{filecontents} 

\begin{filecontents}{plotdata.dat} 
 -3.5 0.350783228 -0.343633445 
 -3.25 0.108195135 -0.107984166 
 -3 -0.141120008 0.140652077 
 -2.75 -0.381660992 0.372462462 
 -2.5 -0.598472144 0.563380821 
 -2.25 -0.778073197 0.701908324 
 -2 -0.909297427 0.789072344 
 -1.75 -0.983985947 0.83271103 
 -1.5 -0.997494987 0.840114882 
 -1.25 -0.948984619 0.812824456 
 -1 -0.841470985 0.745624142 
 -0.75 -0.68163876 0.630066434 
 -0.5 -0.479425539 0.461269555 
 -0.25 -0.247403959 0.244887792 
 0 0 0 
 0.25 0.247403959 -0.244887792 
 0.5 0.479425539 -0.461269555 
 0.75 0.68163876 -0.630066434 
 1 0.841470985 -0.745624142 
 1.25 0.948984619 -0.812824456 
 1.5 0.997494987 -0.840114882 
 1.75 0.983985947 -0.83271103 
 2 0.909297427 -0.789072344 
 2.25 0.778073197 -0.701908324 
 2.5 0.598472144 -0.563380821 
 2.75 0.381660992 -0.372462462 
 3 0.141120008 -0.140652077 
 3.25 -0.108195135 0.107984166 
 3.5 -0.350783228 0.343633445 
\end{filecontents} 

\begin{document} 
\begin{tikzpicture}
\pgfplotsset{xmin=-4.25,xmax=4.25,ymin=-1.25,ymax=1.25}
\begin{axis}
  \addplot [name path global=plot1] table[x index={0}, y index={1}] {plotdata.dat}; 
  \addplot [name path global=plot2] table[x index={0}, y index={2}] {plotdata.dat};
  %% Schnittpunkte der beiden Plots ermitteln:
  \path [name intersections={of=plot1 and plot2,name=i, total=\t}]\foreach \s in {1,...,\t}{(i-\s)
   %node{\s} %Schnittpunktnummern anzeigen
  };
  \coordinate (O) at (rel axis cs:0,0);\coordinate (P) at (rel axis cs:1,1);
\end{axis} 
% Färben:
\foreach \n/\m/\farbe in {O/i-1/green,i-1/i-2/red,i-5/i-6/green,i-6/P/red}{%
  \begin{axis}
    \clip(O-|\n)rectangle(P-|\m);
    \addplot [draw=none,stack plots=y,forget plot] table [y index={1}] {plotdata.dat}; 
    \addplot [draw=none,stack plots=y, fill=\farbe, opacity=.5] table [y expr=\thisrowno{2}-\thisrowno{1}] {plotdata.dat}\closedcycle; 
  \end{axis}
}
\end{tikzpicture} 
\end{document} 
Aus irgendwelchen Gründen werden insgesamt 6 Schnittpunkte gefunden, von denen vier in der Mitte aufeinanderliegen ermittelt. Mit der Option smooth bei den Plots sind es dagegen nur die 3 Schnittpunkte, die wir auch sehen:
\begin{tikzpicture}
\pgfplotsset{xmin=-4.25,xmax=4.25,ymin=-1.25,ymax=1.25}
\begin{axis}
  \addplot [name path global=plot1,smooth] table[x index={0}, y index={1}] {plotdata.dat}; 
  \addplot [name path global=plot2,smooth] table[x index={0}, y index={2}] {plotdata.dat};
  %% Schnittpunkte der beiden Plots ermitteln:
  \path [name intersections={of=plot1 and plot2,name=i, total=\t}]\foreach \s in {1,...,\t}{(i-\s)
   %node{\s} %Schnittpunktnummern anzeigen
  };
  \coordinate (O) at (rel axis cs:0,0);\coordinate (P) at (rel axis cs:1,1);
\end{axis} 
% Färben:
\foreach \n/\m/\farbe in {O/i-1/green,i-1/i-2/red,i-2/i-3/green,i-3/P/red}{%
  \begin{axis}
    \clip(O-|\n)rectangle(P-|\m);
    \addplot [draw=none,stack plots=y,smooth,forget plot] table [y index={1}] {plotdata.dat}; 
    \addplot [draw=none,stack plots=y,smooth,fill=\farbe, opacity=.5] table [y expr=\thisrowno{2}-\thisrowno{1}] {plotdata.dat}\closedcycle; 
  \end{axis}
}
\end{tikzpicture} 
Gruß
Elke

Verfasst: Mi 13. Nov 2013, 17:42
von Mac-Cherony
Vielen Dank für deinen Ansatz Elke, leider funktioniert das nicht immer so allgemein für andere Werte.
Als Übergangslösung generiere ich mir nun die Flächen mit Matlab, da ich meine Daten auch dort erzeuge.

Mit dem Ergebnis stimmt allerdings auch noch etwas nicht. Interessanterweise wird für jede Fläche die ich plotte auch ein Punkt bei y=0 geplottet, der allerdings nicht in den Daten vorhanden ist. Gibt es eine einfache Lösung dafür diesen Punkt nicht zu plotten, so dass wirklich nur die Fläche gefüllt wird?
Als vergleich habe ich einen Plot eingefügt, der direkt in pgfplots erzeugt wird.

Hier ist der Matlabcode mit dem ich die Daten generiere:

x  = [-2:0.01:2]';
y1 = [0.2*x.^3-.05*x.^2+0.2];
y2 = [0.55*x+.13];
Data=[x(:) y1(:) y2(:)];
Diff=y1-y2;

dlmwrite('Data.dat', Data, 'delimiter','\t');
Index=['A';'B';'C';'D';'E';'F';'G';'H';];

VZW = find(diff(sign(Diff)) ~=0);
 if isempty(VZW)==1;
     VZW=length(x);
 else
 end

X=x(1:VZW(1));
Y1=y1(1:VZW(1));
Y2=y2(1:VZW(1));
X = [X,flipud(X)];                
Y = [Y1,flipud(Y2)];

if Diff(1)>0       
        filename=strcat('Data',Index(1,:),'_pos.dat');
        dlmwrite(filename, [X(:) Y(:)], 'delimiter','\t');       
else      
        filename=strcat('Data',Index(1,:),'_neg.dat');
        dlmwrite(filename, [X(:) Y(:)], 'delimiter','\t');        
end

VZW = find(diff(sign(Diff)) ~=0);
 if isempty(VZW)==1;
     VZW=length(x)-1;
 else
 end
 
for n=1:length(VZW)
       
        if n<length(VZW)  

             X=x(VZW(n):VZW(n+1));
             Y1=y1(VZW(n):VZW(n+1));
             Y2=y2(VZW(n):VZW(n+1));
             X = [X,flipud(X)];                
             Y = [Y1,flipud(Y2)];

             if Diff(VZW(n)+1)>0
                filename=strcat('Data',Index(n+1,:),'_pos.dat');
                dlmwrite(filename, [X(:) Y(:)], 'delimiter','\t');
             else
                filename=strcat('Data',Index(n+1,:),'_neg.dat');
                dlmwrite(filename, [X(:) Y(:)], 'delimiter','\t');
             end
        else
             X=x(VZW(n):end);
             Y1=y1(VZW(n):end);
             Y2=y2(VZW(n):end);
             X = [X,flipud(X)];                
             Y = [Y1,flipud(Y2)];

             if Diff(VZW(n)+1)>0
                filename=strcat('Data',Index(n+1,:),'_pos.dat');
                dlmwrite(filename, [X(:) Y(:)], 'delimiter','\t');
             else
                filename=strcat('Data',Index(n+1,:),'_neg.dat');
                dlmwrite(filename, [X(:) Y(:)], 'delimiter','\t');
             end
        end        
end  
und hier das Minimalbeispiel in LaTeX:
\documentclass{standalone} 
\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture} 
\begin{axis} [xmin=-2,xmax=2] 
 
 \addplot [draw=none, fill=red] file {DataA_neg.dat} \closedcycle;
 \addplot [draw=none, fill=green] file {DataB_pos.dat} \closedcycle;
 \addplot [draw=none, fill=red] file {DataC_neg.dat} \closedcycle;
 \addplot [draw=none, fill=green] file {DataD_pos.dat} \closedcycle;
 \addplot [] table[x index={0}, y index={1}] {Data.dat}; 
 \addplot [blue] table[x index={0}, y index={2}] {Data.dat}; 
 \end{axis} 
\end{tikzpicture}

\begin{tikzpicture}

    \begin{axis}[xmin=-2,xmax=2,domain=-2:2,samples=50,stack plots=y]
        \addplot+[mark=none, blue] {.55*x+.13};
        \addplot+[mark=none,fill=green,draw=black] {max(.2*x^3-.05*x^2+.2-(.55*x+.13),0)} \closedcycle;
        \addplot+[mark=none,fill=red,draw=black] {min(.2*x^3-.05*x^2+.2-(.55*x+.13),0)} \closedcycle;
    \end{axis}

\end{tikzpicture}

\end{document}

Verfasst: Mi 13. Nov 2013, 18:52
von Mac-Cherony
Nach etwas stöbern im pgfplots-manual habe ich das Problem gelöst.
\documentclass{standalone} 
\usepackage{pgfplots} 

\begin{document} 

\begin{tikzpicture} 
\begin{axis} [xmin=-2,xmax=2] 
  
 \addplot [draw=none, fill=red] file {DataA_neg.dat} --cycle; 
 \addplot [draw=none, fill=green] file {DataB_pos.dat} --cycle; 
 \addplot [draw=none, fill=red] file {DataC_neg.dat} --cycle; 
 \addplot [draw=none, fill=green] file {DataD_pos.dat} --cycle; 
 \addplot [] table[x index={0}, y index={1}] {Data.dat}; 
 \addplot [blue] table[x index={0}, y index={2}] {Data.dat}; 
 \end{axis} 
\end{tikzpicture} 

\begin{tikzpicture} 

    \begin{axis}[xmin=-2,xmax=2,domain=-2:2,samples=50,stack plots=y] 
        \addplot+[mark=none, blue] {.55*x+.13}; 
        \addplot+[mark=none,fill=green,draw=black] {max(.2*x^3-.05*x^2+.2-(.55*x+.13),0)} \closedcycle; 
        \addplot+[mark=none,fill=red,draw=black] {min(.2*x^3-.05*x^2+.2-(.55*x+.13),0)} \closedcycle; 
    \end{axis} 

\end{tikzpicture} 

\end{document} 

Verfasst: Di 4. Mär 2014, 16:59
von feuersaenger
Hallo,

Version 1.10 von pgfplots ist gerade frisch rausgekommen, und es hat als Hauptmerkmal eine Loesung fuer das Problem, die Flaeche zwischen zwei beliebigen Plots einzufaerben.

Alle alten Loesungen sind immer noch gueltig, aber dies hier kann die Aufgabe gehoerig vereinfachen:
\documentclass{standalone}
\usepackage{pgfplots}

\pgfplotsset{compat=1.10}
\usepgfplotslibrary{fillbetween}
\usepackage{filecontents}

\begin{filecontents}{plotdata.dat}
-3.5 0.350783228 -0.343633445
-3.25 0.108195135 -0.107984166
-3 -0.141120008 0.140652077
-2.75 -0.381660992 0.372462462
-2.5 -0.598472144 0.563380821
-2.25 -0.778073197 0.701908324
-2 -0.909297427 0.789072344
-1.75 -0.983985947 0.83271103
-1.5 -0.997494987 0.840114882
-1.25 -0.948984619 0.812824456
-1 -0.841470985 0.745624142
-0.75 -0.68163876 0.630066434
-0.5 -0.479425539 0.461269555
-0.25 -0.247403959 0.244887792
0 0 0
0.25 0.247403959 -0.244887792
0.5 0.479425539 -0.461269555
0.75 0.68163876 -0.630066434
1 0.841470985 -0.745624142
1.25 0.948984619 -0.812824456
1.5 0.997494987 -0.840114882
1.75 0.983985947 -0.83271103
2 0.909297427 -0.789072344
2.25 0.778073197 -0.701908324
2.5 0.598472144 -0.563380821
2.75 0.381660992 -0.372462462
3 0.141120008 -0.140652077
3.25 -0.108195135 0.107984166
3.5 -0.350783228 0.343633445
\end{filecontents}


\begin{document}
\begin{tikzpicture}
\begin{axis} []
 \addplot [name path=A] table[x index={0}, y index={1}] {plotdata.dat};
 \addplot [name path=B] table[x index={0}, y index={2}] {plotdata.dat};

		\addplot fill between[
			of=A and B,
			split,
			every odd segment/.style={red,left color=red,right color=white},
			every even segment/.style= {green,left color=green, right color=white},
		];
 
\end{axis}
\end{tikzpicture}

\end{document}
Die Loesung basiert auf
\usepgfplotslibrary{fillbetween}
, was die Syntax
\addplot fill between[of=<first> and <second>]
aktiviert. In diesem Fall wollen wir einzelne Segmente des Schnitts identifizieren. Dazu muessen wir diese erstmal ausrechnen lassen, was mithilfe der Option
split
getan wird. Anschliessend koennen wir individuelle Styles fuer
every odd segment
und
every even segment
definieren und nutzen. Zu beachten ist, dass das erste Segmente den Index 0 hat.

Mit liebem Gruss

Christian

PS
Leider funktioniert der Bild upload nicht "Upload Error: Kann das Attachment ./files/4_205.png nicht hochladen. " . Weiss jemand, wie ich bilder anhaengen kann?

Versionsnummer

Verfasst: Di 4. Mär 2014, 17:14
von Stamm-
feuersaenger hat geschrieben:[…] Version 1.0 von pgfplots ist gerade frisch rausgekommen […]
Du meinst sicherlich 1.10, als Nachfolger von 1.9. momentan noch nicht auf CTAN erhältlich. Aber das gibt sich bald.

Verfasst: Di 4. Mär 2014, 20:06
von feuersaenger
Richtig, ich meinte 1.10 - danke!

Die CTAN Mirrorscripte laufen schon (eine Bestaetigung habe ich heute frueh erhalten.

Ich habe meine Antwort editiert sodass sie die richtige Versionsnummer enthaelt.

Mit liebem Gruss

Christian

Verfasst: Mi 12. Mär 2014, 10:39
von unit
Hallo,

der Befehl fill between ist genau das, was ich brauche, danke dafür Christian! Ich habe pgfplots neu installiert um das neuste Package zu kriegen (ich nutze Win7 und Miktex 2.9) und diesen Code aus der Dokumentation von pgfplots (S. 365) getestet:
\documentclass{standalone}
 
\usepackage{pgfplots}
\pgfplotsset{compat=1.10} 
\usepgfplotslibrary{fillbetween}
\usetikzlibrary{pgfplots.fillbetween}
 
\begin{document}
 
test
 
\begin{tikzpicture}
	\begin{axis}
		\addplot[blue,name path=A,domain=0:1] {sqrt(x)};
		\addplot[red, name path=B,domain=0:1] {sqrt(x/2)};
		\addplot[gray] fill between[of=A and B];
	\end{axis}
\end{tikzpicture}

\end{document}


Ich bekomme jedoch die Fehlermeldung

"! Package pgfkeys Error: Choice '1.10' unknown in key '/pgfplots/compat/anchors'. I am going to ignore this key."

nicht nur für anchors, aber für diverse andere keys auch. Laut Miktex Package Manager ist pgfplots vom 07.03.2014 und die Packagegröße ist 16284594 Byte. Ist das nicht die neuste Version 1.10? Kann bitte jemand meinen Code mal testen?

Danke im Voraus!

Grüße
unit

Verfasst: Mi 12. Mär 2014, 12:22
von esdd
Das entspricht der aktuellen Version. Wenn ich die Zeile mit dem \usetikzlibrary{pgfplots.fillbetween} auskommentiere, erhalte ich mit deinem Code auch das erwartete Ergebnis ohne Fehlermeldungen.

Schau mal in deiner log Datei, ob die aktuelle pgfplots Version auch tatsächlich verwendet wird.

Gruß
Elke

Verfasst: Mi 12. Mär 2014, 13:46
von unit
Vielen Dank fürs Schauen!
esdd hat geschrieben:Schau mal in deiner log Datei, ob die aktuelle pgfplots Version auch tatsächlich verwendet wird.
Ich habe eben mal neu compiliert, in der Logdatei steht:

"Package: pgfplots 2013/10/03 v1.9 Data Visualization (1.9)"

Ich verstehe das nicht. Ich habe in der Zwischenzeit sogar versucht das Package manuell zu installieren: tds-Datei heruntergeladen, die Dateien in den MiKTeX-Ordner kopiert, dann "Refresh FNDB" und "Update Formats" durchgeführt. Selbst das updatet das Package nichts.

Was soll ich machen? Bringt eine Neuinstallation von MiKTeX was?

Gruß
unit