
Valori anomali di grande entità, caratteristiche minuscole e picchi netti sono frustrazioni comuni per la visualizzazione dei dati. Tutti e tre possono rendere illeggibili i dettagli visivi schiacciando i componenti della trama in un’area troppo piccola.
A volte è possibile ottenere una soluzione semplicemente escludendo i dati indisciplinati. Quando includere tali dati è la questione principale, applicare una scala logaritmica agli assi può riallineare la spaziatura per una migliore separazione tra i dati di magnitudo inferiore. Questo approccio, tuttavia, non può che arrivare fino a un certo punto.
In questo articolo daremo un’occhiata a un’altra opzione: i grafici zoom, che aumentano la visualizzazione con pannelli che forniscono viste ingrandite delle aree di interesse.
I grafici zoom sono comunemente disposti come inserti nella trama principale, ma possono anche essere combinati come reticolo con la trama originale. Approfondiremo entrambi.
Questo articolo fornisce un’esercitazione orientata al codice su come utilizzare matplotlib con strumenti specializzati di fin dall’inizio libreria per costruire grafici di zoom. Costruiremo una visualizzazione dei dati sulle precipitazioni dal Texas resi disponibili da Evett et al. tramite l’USDA. Questo set di dati comprende un anno intero di letture dei pluviometri di due siti vicini, effettuate a intervalli di 15 minuti.
La breve durata degli eventi piovosi e l’estrema intensità delle precipitazioni più intense complicano le cose. L’inserimento dei dati sulle precipitazioni di un mese di Evett et al. in un semplice grafico a linee rivela il problema di visualizzazione con cui ci troviamo di fronte.
Abbiamo sicuramente del lavoro da fare per migliorare la situazione! Nella nostra visualizzazione, ci concentreremo sul recupero di tre componenti particolari dei dati.
- la piccola doccia intorno al giorno 72,
- il grande temporale intorno al giorno 82, e
- eventi di deboli precipitazioni nel corso dell’intero mese.
Per mostrare meglio questi dettagli, creeremo un pannello zoom per ciascuno.
Il nostro piano è definito, quindi entriamo nel codice 👍
Recupera le registrazioni del pluviometro tramite il Quadro di scienza aperta.
# ----- see appendix for package imports
df = pd.read_csv("https://osf.io/6mx3e/download") # download data
Ecco uno sguardo ai dati.
+------+-------------+--------------+--------------+------------+-----------+
| 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 |
+ ... + ... + ... + ... + ... + ... +
Prima di proseguire, alcune piccole faccende preparatorie.
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"))
Nel codice precedente abbiamo creato nomi di colonne più dettagliati e abbiamo suddiviso i dati in un singolo mese
Il nostro primo passo nel tracciare è inizializzare un file outset.OutsetGrid
esempio per gestire il nostro reticolo di grafici di ingrandimento. Questa classe funziona in modo analogo a di Seaborn FacetGrid
che facilita la costruzione di grafici reticolari standard suddividendo i dati lungo gli assi in base a una variabile categoriale.
OutsetGrid
si differenzia da FacetGrid
però, in quanto oltre agli assi con dati sfaccettati prepara un primo asse “sorgente” contenente tutti i dati insieme. Ulteriore, OutsetGrid
include strumenti per generare automaticamente annotazioni “marquee” che mostrano come gli ingrandimenti corrispondono alla trama originale. Lo schema seguente offre una panoramica OutsetGrid
il modello di trama.
Tornando al nostro esempio, costruiremo un OutsetGrid
fornendo un elenco delle principali regioni della trama che desideriamo ingrandire attraverso il data
kwarg. I kwarg successivi forniscono informazioni sullo stile e sul layout.
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}},
)
Qui abbiamo specificato proporzioni più larghe che alte per le sottotrame e quante colonne vogliamo avere.
La nostra griglia degli assi è impostata, siamo pronti per il passaggio successivo.
È tempo di inserire alcuni contenuti sui nostri assi.
Possiamo utilizzare i grafici dell’area per co-visualizzare le letture dei nostri pluviometri. (Per chi non li conosce, i grafici ad area sono solo grafici a linee con un riempimento fino al X asse.) L’applicazione di un effetto di trasparenza mostrerà elegantemente dove gli indicatori concordano e dove no.
Possiamo sfruttare matplotlib‘S stackplot
per disegnare i nostri grafici ad aree sovrapposte. Sebbene siano progettati per creare grafici con aree “impilate” una sopra l’altra, possiamo ottenere aree sovrapposte suddividendo due chiamate al plotter, una per ciascun indicatore.
Per disegnare lo stesso contenuto su tutti e quattro gli assi della griglia, utilizzeremo OutsetGrid
‘S broadcast
metodo. Questo metodo accetta una funzione plotter come primo argomento, quindi la chiama su ciascun asse utilizzando eventuali argomenti successivi.
# 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,
)
Per un migliore contrasto con i riempimenti dello sfondo, utilizzeremo anche broadcast
per aggiungere uno sfondo bianco attorno agli stackplot.
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
)
Ecco come appare la nostra trama prima di passare alla fase successiva.
Sembra già a posto: in questa fase possiamo già vedere gli ingrandimenti visualizzati sui loro assi corretti.
Ora è il momento di aggiungere riquadri indicatori di zoom, ovvero outset
“marquees”, per mostrare come le scale delle nostre trame ausiliarie si relazionano alla scala della trama principale.
# draw "marquee' zoom indicators showing correspondences between main plot
# and outset plots
grid.marqueeplot(equalize_aspect=False) # allow axes aspect ratios to vary
Nota il kwarg passato per consentire alle trame iniziali di assumere proporzioni diverse dalla trama principale. In questo modo, i dati iniziali possono essere completamente espansi per sfruttare tutto lo spazio disponibile sugli assi.
Siamo quasi arrivati al traguardo: a questo punto mancano solo alcuni ritocchi finali.
La nostra ultima attività è aggiungere una legenda e sostituire il numero X seleziona i timestamp corretti.
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 ciò la trama è completa.
Questo è tutto: una trama zoom in 3 semplici passaggi.
Possiamo creare inserti riorganizzando gli assi del reticolo di ingrandimento in posizione sugli assi principali. Ecco come, utilizzando il file fin dall’inizio biblioteca inset_outsets
attrezzo.
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
)
In questo caso, abbiamo utilizzato anche outset.util.layout_inset_axes
per un controllo preciso sul dimensionamento e sul posizionamento degli inserti.
E proprio così, abbiamo tre inserti di zoom disposti nell’angolo in alto a sinistra.
C’è molto altro che puoi fare con fin dall’inizio.
Oltre alla specifica esplicita dell’area di zoom, il file fin dall’inizio la biblioteca fornisce anche a nato dal mare-API orientata ai dati per dedurre inserti di zoom contenenti sottoinsiemi categoriali di un dataframe. Sono inoltre disponibili ampie opzioni di personalizzazione dello stile e del layout.
Ecco una panoramica di alcuni punti salienti della biblioteca galleria…
Puoi saperne di più sull’utilizzo fin dall’inizio nella documentazione della biblioteca in https://mmore500.com/outset. In particolare, assicurati di controllare il file Guida Rapida.
Outset può essere installato tramite pip as python3 -m pip install outset
.
Questo tutorial è stato contribuito da me, Matteo Andrés Moreno.
Attualmente lavoro come studioso post-dottorato presso la Università del Michigandove il mio lavoro è supportato dalla Eric and Wendy Schmidt AI in Science Postdoctoral Fellowship, un programma Schmidt Futures.
Il mio incarico è diviso tra il Dipartimento di Ecologia e Biologia Evoluzionistica dell’università, il Centro per lo Studio della Complessità e il Michigan Institute for Data Science.
Trovami su Twitter come @MorenoMatthewA e su GitHub come @mmore500.
divulgazione: Sono l’autore del outset
biblioteca.
Evett, Steven R.; Marek, Gary W.; Copeland, Karen S.; Howell, Terry A. Sr.; Colaizzi, Paolo D.; Brauer, David K.; Ruthardt, Brice B. (2023). Evapotraspirazione, irrigazione, rugiada/gelo: dati sul bilancio idrico per i set di dati sulla soia di Bushland, Texas. Ag Data Commons. https://doi.org/10.15482/USDA.ADC/1528713. Accesso nel periodo 2023–12–26.
JD Hunter, “Matplotlib: un ambiente grafico 2D”, Computing in Science & Engineering, vol. 9, n. 3, pagine 90–95, 2007. https://doi.org/10.1109/MCSE.2007.55
Marek, GW, Evett, SR, Colaizzi, PD e Brauer, DK (2021). Coefficienti preliminari del raccolto per la soia di breve stagione piantata tardivamente: Texas High Plains. Agrosistemi, geoscienze e ambiente, 4(2). https://doi.org/10.1002/agg2.20177
Strutture dati per il calcolo statistico in Python, McKinney, Atti della 9a conferenza Python in Science, volume 445, 2010. https://doi.org/ 10.25080/Majora-92bf1922–00a
Waskom, ML, (2021). seaborn: visualizzazione di dati statistici. Giornale del software open source, 6(60), 3021, https://doi.org/10.21105/joss.03021.
Puoi trovare l’intero codice in sintesi Qui e come quaderno Qui.
Per installare le dipendenze per questo esercizio,
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`
Tutte le immagini sono opere dell’autore.
Fonte: towardsdatascience.com