Climogramas (diagramas climáticos) con pgfplots

Climogramas (diagramas climáticos) con pgfplots

Me gustaría crear climogramas (diagramas climáticos) con pgfplots, pero estos gráficos tienen algunas reglas especiales (definidas por Walter y Lieth hace unos 50 años) para la escala del eje que no tengo ni idea de cómo realizar con pgfplots, porque el segundo eje y Se permite que la escala cambie en el medio y las escalas de ambos ejes y se acoplan entre sí. Ejemplos a continuación.

Reglas: El eje y izquierdo (temperatura T) escala en pasos de 10°C comenzando en cero si no existe temperatura bajo cero; de lo contrario, también se extiende en pasos de 10°C bajo cero. Esta sigue siendo la parte fácil (puedo hacerlo), ahora viene la parte difícil: el eje y derecho (cantidad de lluvia) escala con dos veces el valor de T hasta 300 mm. Pero si existe lluvia superior a 300 mm, aumenta en pasos de 200 mm comenzando en 100 mm. El eje derecho nunca baja de cero.

Mi pregunta es: ¿Cuáles son los comandos que tengo que usar para cambiar dinámicamente la escala del eje dependiendo de los valores de entrada y acoplar los ejes entre sí? {x,y}{min,max}Y ticklos amigos ciertamente no son suficientes. Conozco los conceptos básicos del trazado con pgfplots, pero no tengo la menor idea de dónde comenzar con un enfoque de escalado tan dinámico o cuánto de esto tengo que calcular yo mismo (con código propio) y cuánto pgfplotspuedo hacer por mí. Por ejemplo, obviamente necesito analizar los datos de entrada de alguna manera para encontrar los valores máximos, pero no tengo idea de cómo hacerlo en pgfplots.

Para tener algo con qué trabajar, aquí están los datos del primer ejemplo:

#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

Y así es como obtuve el código, pero ahora no sé cómo proceder (los colores, estilos de línea, cuadrícula, etc. no importan por ahora, puedo hacerlo por mi cuenta, solo el escalado/acoplamiento del eje es el problema):

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

Ej. 1: Escalado de los cambios del eje de lluvia (porque > 300 mm), solo T > 0 ingrese la descripción de la imagen aquí

Ejemplo 2: el eje de lluvia no cambia de escala porque siempre es <300 mm. T < 0. ingrese la descripción de la imagen aquí

Respuesta1

Lo que necesitas es posible con una configuración de estilo que

  • configura la transformación no lineal para el eje de lluvia
  • calcula límites de eje adecuados (acoplados) para ambos ejes y
  • calcula unidades adecuadas (acopladas) para ambos ejes y
  • requiere entrada manual para decidir dónde comienzan/terminan ambos ejes. Por lo que entiendo de su caso de uso, esta entrada consta de la temperatura mínima y el nivel máximo de lluvia (el otro límite siempre está implícitamente limitado).

Llegué al siguiente prototipo avanzado:

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

ingrese la descripción de la imagen aquí

Creo que ya es bastante bueno, aunque le quedan un par de elementos, principalmente el formato y las rutas de relleno.

Y hay un problema abierto que en realidad es una "característica faltante" en pgfplots: tan pronto como uno escribe axis x line=middle, pgfplots no muestra la primera y la última etiqueta de marca. Muy desafortunado para esta aplicación. Lo incluiré en la lista de tareas pendientes de pgfplots. Quizás deberías deshacerlo axis x line=middlepara solucionar este problema. Tenga en cuenta que mostrar x ticks como intervalo significa que tiene n-1 etiquetas de tick para n posiciones de tick. En resumen: es posible que necesite replicar el punto de datos de enero de alguna manera.

información relacionada