Klimagraphen (Klimadiagramme) mit pgfplots

Klimagraphen (Klimadiagramme) mit pgfplots

Ich möchte mit Klimagraphen (Klimadiagramme) erstellen pgfplots, aber diese Graphen haben einige spezielle Regeln (definiert von Walter und Lieth vor etwa 50 Jahren) für die Achsenskalierung, und ich habe absolut keine Ahnung, wie ich sie mit umsetzen soll pgfplots, da sich die Skalierung der zweiten Y-Achse zwischendurch ändern darf und die Skalierungen beider Y-Achsen aneinander gekoppelt sind. Beispiele unten.

Regeln: Die linke y-Achse (Temperatur T) skaliert in 10°C-Schritten beginnend bei Null, wenn keine Temperatur unter Null existiert, andernfalls dehnt sie sich ebenfalls in 10°C-Schritten aus. Das ist noch der einfache Teil (das kann ich), jetzt kommt der schwierige Teil: Die rechte y-Achse (Regenmenge) skaliert mit dem doppelten Wert von T bis 300mm. Wenn jedoch Regen über 300mm existiert, skaliert sie in 200mm-Schritten beginnend bei 100mm. Die rechte Achse geht nie unter Null.

Meine Frage ist: Welche Befehle muss ich verwenden, um die Achsenskalierung je nach Eingabewerten dynamisch zu ändern und die Achsen aneinander zu koppeln? {x,y}{min,max}, tickund Freunde reichen sicherlich nicht aus. Ich kenne die Grundlagen des Plottens mit pgfplots, aber ich habe absolut keine Ahnung, wo ich mit einem solchen dynamischen Skalierungsansatz anfangen soll oder wie viel davon ich selbst berechnen muss (mit eigenem Code) und wie viel ich pgfplotsfür mich tun kann. Beispielsweise muss ich die Eingabedaten offensichtlich irgendwie analysieren, um Maximalwerte zu finden, aber ich habe keine Ahnung, wie das in geht pgfplots.

Damit Sie etwas zum Arbeiten haben, hier die Daten aus dem ersten Beispiel:

#bombay.txt
#M T/°C N/mm
 1 23.9    3
 2 23.9    3
 3 26.1    3
 4 28.1    2
 5 29.7   18
 6 28.9  485
 7 27.2  617
 8 27.0  340
 9 27.0  264
10 28.1   64
11 27.2   13
12 25.6    3

Und so bin ich nun codemäßig vorgedrungen, weiß jetzt aber nicht weiter (Farben, Linienarten, Raster etc. sind erstmal egal, das bekomme ich alleine hin, nur die Achsenskalierung/-kopplung ist das Problem):

\documentclass{standalone}
\usepackage{pgfplots} \pgfplotsset{compat=newest}
\usepackage{siunitx}
\begin{document}
\begin{tikzpicture}
  \def\filename{bombay.txt}
  \def\monthnames{{"J","F","Mar","A","May","Jun","Jul","A","S","O","N","D"}}
  \begin{axis}[
        ylabel={Temperature in \si{\celsius}},
        tick pos=left,
        xticklabel={\pgfmathparse{\monthnames[Mod(\tick-1,12)]}\pgfmathresult},
        xmin=1, xmax=12,
        ymin=0, ytick={0,10,20,30,40,50}]
    \addplot+[red, mark=none] table [x index=0, y index=1] {\filename};
  \end{axis}
    \begin{axis}[
        ylabel={Rain in \si{\milli\metre}},
        axis y line*=right,
        hide x axis,
        xmin=1, xmax=12, ymin=0]
    \addplot+[blue, mark=none] table [x index=0, y index=2] {\filename};
  \end{axis}
\end{tikzpicture}
\end{document}

Bsp. 1: Skalierung der Regenachse ändert sich (weil > 300mm), nur T > 0 Bildbeschreibung hier eingeben

Beispiel 2: Die Skalierung der Regenachse ändert sich nicht, da sie immer < 300 mm ist. T < 0. Bildbeschreibung hier eingeben

Antwort1

Was Sie brauchen, ist möglich mit einem Stil-Setup, das

  • konfiguriert die nichtlineare Transformation für die Regenachse
  • berechnet passende (gekoppelte) Achsengrenzen für beide Y-Achsen
  • berechnet passende (gekoppelte) Einheiten für beide y-Achsen
  • erfordert manuelle Eingabe, um zu entscheiden, wo beide Achsen beginnen/enden. Soweit ich Ihren Anwendungsfall verstehe, besteht diese Eingabe aus der Mindesttemperatur und dem maximalen Niederschlagsniveau (die andere Grenze ist immer implizit gebunden).

Ich bin zu folgendem fortgeschrittenen Prototyp gekommen:

\documentclass{standalone}
\usepackage{pgfplots} 
\pgfplotsset{compat=1.9}

  % The following \pgfplotsset macro defines a *manual* wheather
  % visualization system which couples temperature and rain axis.
  \pgfplotsset{
     rain trafo/.style={
        y coord trafo/.code={%
            \pgfmathparse{##1 > 100 ? 100 + 0.1*(##1 - 100) : ##1}%
        },%
        y coord inv trafo/.code={%
            \pgfmathparse{##1 > 100 ? 100 + 10*(##1 - 100) : ##1}%
        },%
        %
        % configure the allowed tick positions for the rain
        % (UNTRANSFORMED!):
        ytick={0,20,...,100,300,500,...,1000},
     },
     %
     /wheather/temp min/.initial=,
     /wheather/rain max/.initial=,
     %
     % USAGE: \pgfkeys{/wheather/set up={temp min=<min temperature>, rain max=<max rain axis value>}
     % It will set up two styles "temp axis" and "rain axis". These
     % styles will contain suitable axis limits and unit scales.
     /wheather/set up/.code={%
        \pgfkeysalso{/wheather/.cd,#1}%
        %
        % these values are supposed to be given:
        \pgfkeysgetvalue{/wheather/rain max}\rainMax
        \pgfkeysgetvalue{/wheather/temp min}\temperatureMin
        %
        % make sure they are given!
        \ifx\rainMax\empty
            \PackageError{wheather}{Please provide 'rain max=MAX RAIN VALUE IN AXIS'}{}%
        \fi
        \ifx\temperatureMin\empty
            \PackageError{wheather}{Please provide 'temp min=MIN TEMPERATURE VALUE IN AXIS'}{}%
        \fi
        %
        % now, compute the MISSING values. These are rain min and
        % temperature max....
        \begingroup
        % do this in a group such that 'rain trafo' is only applied
        % within the group...
        % 
        % store it - just in case 'y coord trafo' is empty.
        \let\pgfmathresult=\rainMax
        %
        \pgfplotsset{rain trafo,y coord trafo={\rainMax}}%
        \global\let\rainMaxTransformed=\pgfmathresult
        \pgfmathparse{\pgfmathresult /2}%
        \global\let\temperatureMax=\pgfmathresult
        \endgroup
        %
        \pgfmathparse{2*(\pgfkeysvalueof{/wheather/temp min})}%
        \let\rainMin=\pgfmathresult
        %
        % now we want to compute a suitable axis scale for both axes.
        % To this end, we scale them such that they fit into the value
        % of  \axisdefaultheight :
        \pgfmathparse{\axisdefaultheight/(\temperatureMax-\temperatureMin)}%
        \let\temperatureUnit=\pgfmathresult
        %
        \pgfmathparse{\axisdefaultheight/(\rainMaxTransformed-\rainMin)}%
        \let\rainUnit=\pgfmathresult
        %
        % OK, compute the result:
        \pgfplotsset{%
            set layers,
            %
            % this is to be used for the temperature axis:
             temp axis/.style={
                y=\temperatureUnit,
                ymin=\temperatureMin,
                ymax=\temperatureMax,
                %
                % configure the allowed tick positions for a temperature:
                ytick={-40,-30,-20,...,140},
                % ... but only show the *label* for "small"
                % temperatures:
                yticklabel={%
                    \ifdim\tick pt<45pt 
                        \pgfmathprintnumber\tick
                    \fi
                },
                ylabel={Temperature},
                %tick pos=left,% seems to fail for 'axis x line=middle'!?
                %
                % configure the x axis:
                axis x line*=middle,
                xmin=1, xmax=12,
                xtick={1,2,...,12},
                xticklabels={J,F,M,A,M,J,J,A,S,O,N,D},
                x tick label as interval,
                grid=major,
             },
             %
             % ... and this is to be used for the rain axis:
             rain axis/.style={
                y=\rainUnit,
                ymin=\rainMin,
                ymax=\rainMax,
                % this range will be overwritten by 'rain trafo' if it
                % is active:
                ytick={0,20,...,400},
                rain trafo,
                yticklabel style={/pgf/number format/precision=0},
                ylabel={Rain},
                axis y line*=right,
                hide x axis,
                xmin=1, xmax=12,
             },
        }%
     },
  }

\begin{filecontents}{bombay.txt}
#bombay.txt
#M T/°C N/mm
 1 23.9    3
 2 23.9    3
 3 26.1    3
 4 28.1    2
 5 29.7   18
 6 28.9  485
 7 27.2  617
 8 27.0  340
 9 27.0  264
10 28.1   64
11 27.2   13
12 25.6    3
\end{filecontents}

\begin{filecontents}{moskow.txt}
#M T/°C N/mm
 1 -10.9    30
 2 -12.9    23
 3 -6.1    30
 4 3.1    40
 5 6.7   50
 6 15.9  60
 7 20.2  61
 8 22.0  50
 9 17.0  45
10 10.1   20
11 3.2   22
12 -9.6    25
\end{filecontents}

\begin{document}
\begin{tikzpicture}

  \pgfplotsset{/wheather/set up={temp min=0, rain max=800}}

  \def\filename{bombay.txt}

  \begin{axis}[
        temp axis,
    ]
    \addplot+[red, mark=none] table [x index=0, y index=1] {\filename};
  \end{axis}
    \begin{axis}[
        rain axis,
    ]
    \addplot+[blue, mark=none] table [x index=0, y index=2] {\filename};
  \end{axis}
\end{tikzpicture}

\begin{tikzpicture}

  \pgfplotsset{
     *clear* the nonlinear rain trafo. We do not want it here:
     rain trafo/.style=,
     /wheather/set up={temp min=-20, rain max=160},
  }

  \def\filename{moskow.txt}

  \begin{axis}[
        temp axis,
    ]
    \addplot+[red, mark=none] table [x index=0, y index=1] {\filename};
  \end{axis}
    \begin{axis}[
        rain axis,
    ]
    \addplot+[blue, mark=none] table [x index=0, y index=2] {\filename};
  \end{axis}
\end{tikzpicture}
\end{document}

Bildbeschreibung hier eingeben

Ich glaube, es ist schon ziemlich gut, obwohl noch ein paar Dinge fehlen – hauptsächlich die Formatierung und die Füllpfade.

Und es gibt noch ein offenes Problem, bei dem es sich eigentlich um eine „fehlende Funktion“ in pgfplots handelt: Sobald man schreibt axis x line=middle, zeigt pgfplots nicht die erste und letzte Teilstrichbeschriftung an. Sehr schade für diese Anwendung. Ich werde das auf die To-do-Liste für pgfplots setzen. Vielleicht sollten Sie dies rückgängig machen, axis x line=middleum es zu beheben. Bitte beachten Sie, dass die Anzeige von x Teilstrichen als Intervall bedeutet, dass Sie n-1 Teilstrichbeschriftungen für n Teilstrichpositionen haben. Kurz gesagt: Sie müssen den Datenpunkt für Januar möglicherweise irgendwie replizieren.

verwandte Informationen