Los valores atípicos de gran magnitud, las características diminutas y los picos pronunciados son frustraciones comunes en la visualización de datos. Los tres pueden hacer que los detalles visuales sean ilegibles al reducir los componentes de la trama en un área demasiado pequeña.
A veces se puede solucionar simplemente excluyendo los datos rebeldes. Cuando incluir dichos datos es principal para una pregunta en cuestión, aplicar una escala logarítmica a los ejes puede realinear el espaciado para una mejor separación entre datos de menor magnitud. Sin embargo, este enfoque sólo puede llegar hasta cierto punto.
En este artículo, veremos otra opción: gráficos con zoom, que aumentan una visualización con paneles que brindan vistas ampliadas de áreas de interés.
Los gráficos ampliados suelen organizarse como inserciones en el gráfico principal, pero también se pueden combinar como un entramado con el gráfico original. Profundizaremos en ambos.
Este artículo proporciona un tutorial orientado al código sobre cómo utilizar matplotlib con herramientas especializadas del comienzo biblioteca para construir gráficos de zoom. Construiremos una visualización de los datos de lluvia de Texas disponibles por Evett y cols. a través del USDA. Este conjunto de datos comprende un año completo de lecturas de pluviómetros de dos sitios cercanos, tomadas en intervalos de 15 minutos.
La corta duración de las lluvias y la extrema intensidad de las precipitaciones más intensas complican las cosas. Combinar los datos de lluvia de Evett et al. de un mes en un diagrama lineal simple revela el problema de visualización al que nos enfrentamos.
¡Ciertamente tenemos trabajo que hacer para mejorar esto! En nuestra visualización, nos centraremos en recuperar tres componentes particulares de los datos.
- la pequeña lluvia alrededor del día 72,
- la gran tormenta alrededor del día 82, y
- Eventos de precipitaciones ligeras durante todo el mes.
Para mostrar mejor estos detalles, crearemos un panel de zoom para cada uno.
Nuestro plan está diseñado, así que entremos en el código 👍
Obtenga los registros del pluviómetro a través del Marco de ciencia abierta.
# ----- see appendix for package imports
df = pd.read_csv("https://osf.io/6mx3e/download") # download data
He aquí un vistazo a los datos.
+------+-------------+--------------+--------------+------------+-----------+
| Year | Decimal DOY | NW dew/frost | SW dew/frost | NW precip | SW precip |
+------+-------------+--------------+--------------+------------+-----------+
| 2019 | 59.73958 | 0 | 0 | 0 | 0 |
| 2019 | 59.74999 | 0 | 0 | 0.06159032 | 0 |
| 2019 | 59.76041 | 0 | 0 | 0 | 0 |
| 2019 | 59.77083 | 0 | 0 | 0.05895544 | 0.0813772 |
| 2019 | 59.78124 | 0 | 0 | 0.05236824 | 0.0757349 |
+ ... + ... + ... + ... + ... + ... +
Antes de continuar, algunas tareas preparatorias menores.
nwls = "NW Lysimeter\n(35.18817624°N, -102.09791°W)"
swls = "SW Lysimeter\n(35.18613985°N, -102.0979187°W)"
df[nwls], df[swls] = df["NW precip in mm"], df["SW precip in mm"]# filter down to just data from March 2019
march_df = df[np.clip(df["Decimal DOY"], 59, 90) == df["Decimal DOY"]]
En el código anterior, creamos nombres de columnas más detallados y subdividimos los datos en un solo mes.
Nuestro primer paso de trazado es inicializar un outset.OutsetGrid ejemplo para gestionar nuestro entramado de gráficos de ampliación. Esta clase opera de manera análoga a nacido en el mar FacetGridque facilita la construcción de gráficos de celosía estándar al dividir los datos en ejes en función de una variable categórica.
OutsetGrid difiere de FacetGridSin embargo, además de los ejes con datos facetados, prepara ejes de “fuente” iniciales que contienen todos los datos juntos. Más, OutsetGrid incluye herramientas para generar automáticamente anotaciones de “marquesina” que muestran cómo las ampliaciones corresponden al gráfico original. El siguiente esquema resume OutsetGridEl modelo de trazado.
Volviendo a nuestro ejemplo, construiremos un OutsetGrid proporcionando una lista de las principales regiones de la trama que queremos ampliar a través del datakwarg. Los kwargs posteriores proporcionan información sobre estilo y diseño.
grid = otst.OutsetGrid( # initialize axes grid manager
data=[
# (x0, y0, x1, y1) regions to outset
(71.6, 0, 72.2, 2), # little shower around day 72
(59, 0, 90, 0.2), # all light precipitation events
(81.3, 0, 82.2, 16), # big rainstorm around day 82
],
x="Time", # axes label
y="Precipitation (mm)", # axes label
aspect=2, # make subplots wide
col_wrap=2, # wrap subplots into a 2x2 grid
# styling for zoom indicator annotations, discussed later
marqueeplot_kws={"frame_outer_pad": 0, "mark_glyph_kws": {"zorder": 11}},
marqueeplot_source_kws={"zorder": 10, "frame_face_kws": {"zorder": 10}},
)
Aquí hemos especificado una relación de aspecto más ancha que alta para las subtramas y cuántas columnas queremos tener.
Nuestra cuadrícula de ejes está configurada, estamos listos para el siguiente paso.
Es hora de poner algo de contenido en nuestros ejes.
Podemos utilizar gráficos de área para covisualizar las lecturas de nuestros pluviómetros. (Para aquellos que no están familiarizados, los gráficos de área son simplemente gráficos de líneas con un relleno hasta el final). X eje.) La aplicación de un efecto de transparencia mostrará elegantemente dónde coinciden los indicadores y dónde no.
podemos aprovechar matplotlib‘s stackplotpara dibujar nuestros gráficos de áreas superpuestas. Aunque está diseñado para crear gráficos con áreas “apiladas” una encima de otra, podemos obtener áreas superpuestas dividiendo dos llamadas al trazador, una para cada calibre.
Para dibujar este mismo contenido en los cuatro ejes de la cuadrícula, usaremos OutsetGrid‘s broadcast método. Este método toma una función de trazador como primer argumento y luego la llama en cada eje utilizando los argumentos posteriores.
# draw semi-transparent filled lineplot on all axes for each lysimeter
for y, color in zip([nwls, swls], ["fuchsia", "aquamarine"]):
grid.broadcast(
plt.stackplot, # plotter
march_df["Decimal DOY"], # all kwargs below forwarded to plotter...
march_df[y],
colors=[color],
labels=[y],
lw=2,
edgecolor=color,
alpha=0.4, # set to 60% transparent (alpha 1.0 is non-transparent)
zorder=10,
)
Para un mejor contraste con los rellenos de fondo, también usaremos broadcast para agregar una base blanca alrededor de los diagramas de pila.
grid.broadcast(
plt.stackplot, # plotter
march_df["Decimal DOY"], # all kwargs below forwarded to plotter...
np.maximum(march_df["SW precip in mm"], march_df["NW precip in mm"]),
colors=["white"],
lw=20, # thick line width causes protrusion of white border
edgecolor="white",
zorder=9, # note lower zorder positions underlay below stackplots
)
Así es como se ve nuestra trama antes de pasar a la siguiente etapa.
Ya se ve bien: ya podemos ver aumentos que aparecen en sus ejes adecuados en esta etapa.
Ahora es el momento de agregar cuadros indicadores de zoom, también conocidos como outset “marquesinas”, para mostrar cómo las escalas de nuestras parcelas auxiliares se relacionan con la escala de la trama principal.
# draw "marquee' zoom indicators showing correspondences between main plot
# and outset plots
grid.marqueeplot(equalize_aspect=False) # allow axes aspect ratios to vary
Tenga en cuenta el kwarg pasado para permitir que las tramas iniciales adopten proporciones de aspecto diferentes a las de la trama principal. De esta manera, los datos iniciales se pueden expandir completamente para aprovechar todo el espacio de ejes disponible.
Ya hemos recorrido la mayor parte del camino; en este punto solo quedan algunos toques finales.
Nuestra última tarea es agregar una leyenda y cambiar los números. X marca las marcas de tiempo adecuadas.
grid.source_axes.legend( # add legend to primary axes
loc="upper left",
bbox_to_anchor=(0.02, 1.0), # legend positioning
frameon=True, # styling: turn on legend frame
)# ----- see appendix for code to relabel axes ticks with timestamps
Con eso, la trama está completa.
Eso es todo lo que hay que hacer, un gráfico ampliado en 3 sencillos pasos.
Podemos crear inserciones reorganizando los ejes de la red de aumento en su posición sobre los ejes principales. Así es como, usando el comienzo biblioteca inset_outsets herramienta.
otst.inset_outsets(
grid,
insets=otst_util.layout_corner_insets(
3, # three insets
"NW", # arrange in upper-left corner
inset_margin_size=(0.02, 0), # allow closer to main axes bounds
inset_grid_size=(0.67, 0.9), # grow to take up available space
),
equalize_aspect=False,
)
sns.move_legend( # move legend centered above figure
grid.source_axes, "lower center", bbox_to_anchor=(0.5, 1.1), ncol=2
)
En este caso también hemos utilizado outset.util.layout_inset_axes para un control preciso sobre el tamaño y posicionamiento de las inserciones.
Y así, tenemos tres inserciones de zoom dispuestas en la esquina superior izquierda.
Hay mucho más con lo que puedes hacer comienzo.
Además de la especificación explícita del área de zoom, el comienzo La biblioteca también ofrece una nacido en el mar-API orientada a datos para inferir inserciones de zoom que contienen subconjuntos categóricos de un marco de datos. También se encuentran disponibles amplias opciones de personalización de diseño y estilo.
He aquí un vistazo a algunos aspectos destacados de la biblioteca. galería…
Puedes aprender más sobre el uso comienzo en la documentación de la biblioteca en https://mmore500.com/outset. En particular, asegúrese de revisar el guía de inicio rápido.
Outset se puede instalar a través de pip como python3 -m pip install outset.
Este tutorial es una contribución mía, Mateo Andrés Moreno.
Actualmente me desempeño como becario postdoctoral en la Universidad de Michigandonde mi trabajo cuenta con el apoyo de la beca postdoctoral Eric and Wendy Schmidt AI in Science, un programa Schmidt Futures.
Mi nombramiento se divide entre el Departamento de Ecología y Biología Evolutiva de la universidad, el Centro para el Estudio de la Complejidad y el Instituto de Ciencia de Datos de Michigan.
Encuéntrame en Twitter como @MorenoMatthewA y en GitHub como @mmore500.
divulgación: soy el autor del outset biblioteca.
Evett, Steven R.; Marek, Gary W.; Copeland, Karen S.; Howell, Terry A. Sr.; Colaizzi, Paul D.; Brauer, David K.; Ruthardt, Brice B. (2023). Evapotranspiración, riego, rocío/escarcha: datos de balance hídrico para conjuntos de datos de soja de Bushland, Texas. Bienes comunes de datos agrícolas. https://doi.org/10.15482/USDA.ADC/1528713. Consultado el 26 de diciembre de 2023.
JD Hunter, “Matplotlib: un entorno de gráficos 2D”, Computing in Science & Engineering, vol. 9, núm. 3, págs. 90–95, 2007. https://doi.org/10.1109/MCSE.2007.55
Marek, GW, Evett, SR, Colaizzi, PD y Brauer, DK (2021). Coeficientes preliminares de cultivo para soja de temporada corta sembrada tardíamente: Texas High Plains. Agrosistemas, Geociencias y Medio Ambiente, 4(2). https://doi.org/10.1002/agg2.20177
Estructuras de datos para computación estadística en Python, McKinney, Actas de la novena conferencia Python in Science, volumen 445, 2010. https://doi.org/ 10.25080/Majora-92bf1922–00a
Waskom, ML, (2021). seaborn: visualización de datos estadísticos. Revista de software de código abierto, 6(60), 3021, https://doi.org/10.21105/joss.03021.
Puede encontrar el código completo como esencia. aquí y como cuaderno aquí.
Para instalar dependencias para este ejercicio,
python3 -m pip install \
matplotlib `# ==3.8.2`\
numpy `# ==1.26.2` \
outset `# ==0.1.6` \
opytional `# ==0.1.0` \
pandas `# ==2.1.3` \
seaborn `# ==0.13.0`
Todas las imágenes son obras del autor.