MEP28: Rimuovi la complessità da Axes.boxplot #
Stato n.
Discussione
Filiali e Pull request #
Di seguito sono elencati eventuali PR o filiali aperti relativi a questo deputato europeo:
Depreca i kwarg statistici ridondanti in
Axes.boxplot
: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecationsDepreca le opzioni di stile ridondanti in
Axes.boxplot
: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecationsDeprecare il passaggio degli array NumPy 2D come input: Nessuno
Aggiungi opzioni di pre e post-elaborazione a
cbook.boxplot_stats
: https://github.com/phobson/matplotlib/tree/boxplot-stat-transformsEsposizione
cbook.boxplot_stats
tramiteAxes.boxplot
kwargs: nessunaRimuovi i kwarg statistici ridondanti in
Axes.boxplot
: NessunoRimuovi le opzioni di stile ridondanti in
Axes.boxplot
: NessunoElementi rimanenti emersi dalla discussione: nessuno
Estratto n.
Nelle ultime versioni, il Axes.boxplot
metodo è cresciuto in complessità per supportare lo stile dell'artista e il calcolo statistico completamente personalizzabili. Questo ha portato ad Axes.boxplot
essere diviso in più parti. Le statistiche necessarie per disegnare un boxplot sono calcolate in
cbook.boxplot_stats
, mentre gli artisti effettivi sono disegnati da Axes.bxp
. Il metodo originale Axes.boxplot
rimane l'API più pubblica che gestisce il passaggio dei dati forniti dall'utente a cbook.boxplot_stats
, l'alimentazione dei risultati a Axes.bxp
e le informazioni sullo stile di pre-elaborazione per ogni aspetto dei grafici a scatola.
Questo deputato delineerà un percorso in avanti per ripristinare la complessità aggiunta e semplificare l'API mantenendo una ragionevole compatibilità con le versioni precedenti.
Descrizione dettagliata #
Attualmente, il Axes.boxplot
metodo accetta parametri che consentono agli utenti di specificare mediane e intervalli di confidenza per ogni riquadro che verrà disegnato nel grafico. Questi sono stati forniti in modo che gli utenti avanzati potessero fornire statistiche calcolate in modo diverso rispetto al semplice metodo fornito da matplotlib. Tuttavia, la gestione di questo input richiede una logica complessa per garantire che le forme della struttura dei dati corrispondano a ciò che deve essere disegnato. Al momento, quella logica contiene 9 istruzioni if/else separate annidate fino a 5 livelli di profondità con un ciclo for e può generare fino a 2 errori. Questi parametri sono stati aggiunti prima della creazione del Axes.bxp
metodo, che disegna grafici a scatole da un elenco di dizionari contenenti le statistiche pertinenti. Matplotlib fornisce anche una funzione che calcola queste statistiche tramitecbook.boxplot_stats
. Si noti che gli utenti avanzati possono ora a) scrivere la propria funzione per calcolare le statistiche richieste da
Axes.bxp
oppure b) modificare l'output restituito da cbook.boxplots_stats
per personalizzare completamente la posizione degli artisti delle trame. Con questa flessibilità, i parametri per specificare manualmente solo le mediane e i relativi intervalli di confidenza rimangono compatibili con le versioni precedenti.
Più o meno nello stesso periodo in cui i due ruoli di Axes.boxplot
sono stati divisi in
cbook.boxplot_stats
per il calcolo e Axes.bxp
per il disegno, entrambi
Axes.boxplot
sono Axes.bxp
stati scritti per accettare parametri che alternano individualmente il disegno di tutti i componenti dei boxplot e parametri che configurano individualmente lo stile di quegli artisti. Tuttavia, per mantenere la retrocompatibilità, il sym
parametro (precedentemente utilizzato per specificare il simbolo dei volantini) è stato mantenuto. Questo parametro stesso richiede una logica piuttosto complessa per riconciliare i sym
parametri con il flierprops
parametro più recente nello stile predefinito specificato da matplotlibrc
.
Questo MEP cerca di semplificare notevolmente la creazione di boxplot sia per i principianti che per gli utenti avanzati. È importante sottolineare che le modifiche qui proposte saranno disponibili anche per i pacchetti downstream come seaborn, poiché seaborn consente in modo intelligente agli utenti di passare dizionari arbitrari di parametri attraverso l'API seaborn alle funzioni matplotlib sottostanti.
Ciò sarà ottenuto nel seguente modo:
cbook.boxplot_stats
verrà modificato per consentire il passaggio di funzioni di trasformazione pre e post calcolo (ad esempio,np.log
enp.exp
per dati distribuiti in modo lognormale)
Axes.boxplot
sarà modificato per accettarli e passarli ingenuamente acbook.boxplots_stats
(Alt: passa la funzione stat e un dict dei suoi parametri opzionali).I parametri obsoleti di
Axes.boxplot
verranno deprecati e successivamente rimossi.
Importanza #
Poiché i limiti dei baffi sono calcolati aritmeticamente, c'è un presupposto implicito di normalità nei grafici a scatola e baffi. Ciò influisce principalmente su quali punti dati sono classificati come valori anomali.
Consentire le trasformazioni ai dati e ai risultati utilizzati per disegnare grafici a scatole consentirà agli utenti di rinunciare a tale ipotesi se è noto che i dati non si adattano a una distribuzione normale.
Di seguito è riportato un esempio di come Axes.boxplot
classifica i valori anomali dei dati lognormali in modo diverso a seconda di uno di questi tipi di trasformazioni.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cbook
np.random.seed(0)
fig, ax = plt.subplots(figsize=(4, 6))
ax.set_yscale('log')
data = np.random.lognormal(-1.75, 2.75, size=37)
stats = cbook.boxplot_stats(data, labels=['arithmetic'])
logstats = cbook.boxplot_stats(np.log(data), labels=['log-transformed'])
for lsdict in logstats:
for key, value in lsdict.items():
if key != 'label':
lsdict[key] = np.exp(value)
stats.extend(logstats)
ax.bxp(stats)
fig.show()
( Codice sorgente , png )
Implementazione n.
Passaggio delle funzioni di trasformazione a cbook.boxplots_stats
#
Questo deputato propone che due parametri (ad esempio, transform_in
e
transform_out
vengano aggiunti alla funzione ricettario che calcola le statistiche per la funzione boxplot. Questi saranno argomenti facoltativi solo per parole chiave e possono essere facilmente impostati come no-op se omessi dall'utente. La funzione verrà applicata ai dati mentre la funzione scorre attraverso ogni sottoinsieme dei dati passati ad esso.Dopo che l'elenco dei dizionari statistici è stato calcolato, la
funzione viene applicata a ciascun valore nei dizionari.lambda x: x
transform_in
boxplot_stats
transform_out
Queste trasformazioni possono quindi essere aggiunte alla firma della chiamata
Axes.boxplot
con un impatto minimo sulla complessità del metodo. Questo perché possono essere passati direttamente a cbook.boxplot_stats
. In alternativa, Axes.boxplot
potrebbe essere modificato per accettare una funzione statistica facoltativa kwarg e un dizionario di parametri da passare direttamente ad essa.
A questo punto dell'implementazione, gli utenti e le librerie esterne come Seaborn avrebbero il controllo completo tramite il Axes.boxplot
metodo. Ancora più importante, come minimo, Seaborn non richiederebbe modifiche alla sua API per consentire agli utenti di sfruttare queste nuove opzioni.
Semplificazioni Axes.boxplot
all'API e ad altre funzioni #
La semplificazione del metodo boxplot consiste principalmente nel deprecare e quindi rimuovere i parametri ridondanti. Facoltativamente, un passaggio successivo includerebbe la rettifica di incoerenze terminologiche minori tra Axes.boxplot
e Axes.bxp
.
I parametri da deprecare e rimuovere includono:
usermedians
- elaborato da 10 SLOC, 3if
blocchi, unfor
loop
conf_intervals
- gestito da 15 SLOC, 6if
blocchi, unfor
loop
sym
- elaborato da 12 SLOC, 4if
blocchi
La rimozione sym
dell'opzione consente di spostare in Axes.bxp
. Ciò non elimina alcuna complessità, ma rafforza il principio di responsabilità unica tra Axes.bxp
, cbook.boxplot_stats
, e Axes.boxplot
.
Inoltre, il notch
parametro potrebbe essere rinominato shownotches
per essere coerente con Axes.bxp
. Questo tipo di pulizia potrebbe essere fatto un ulteriore passo avanti e il whis
, bootstrap
, autorange
potrebbe essere inserito nei kwargs passati al nuovo statfxn
parametro.
Compatibilità con le versioni precedenti #
L'implementazione di questo MEP comporterebbe alla fine la deprecazione incompatibile con le versioni precedenti e quindi la rimozione dei parametri delle parole chiave
usermedians
, conf_intervals
e sym
. Ricerche superficiali su GitHub hanno indicato che usermedians
, conf_intervals
sono utilizzati da pochi utenti, che sembrano avere tutti una conoscenza molto approfondita di matplotlib. Un robusto ciclo di deprecazione dovrebbe fornire tempo sufficiente a questi utenti per migrare a una nuova API.
La deprecazione di sym
, tuttavia, potrebbe avere una portata molto più ampia nella base utenti di matplotlib.
Programma #
Una sequenza temporale accelerata potrebbe essere simile alla seguente:
v2.0.1 aggiungi trasformazioni a
cbook.boxplots_stats
, esponi inAxes.boxplot
v2.1.0 Deprecazioni iniziali e utilizzo di array NumPy 2D come input
Utilizzo di array NumPy 2D come input. La semantica degli array 2D è generalmente confusa.
usermedians
,conf_intervals
,sym
parametri
v2.2.0
rimuovi
usermedians
,conf_intervals
,sym
parametrideprecare
notch
a favore dishownotches
essere coerente con altri parametri eAxes.bxp
- v2.3.0
rimuovere il
notch
parametrospostare tutta la logica di commutazione dello stile e dell'artista in
Axes.bxp
taleAxes.boxplot
è poco più di un intermediario traAxes.bxp
ecbook.boxplots_stats
Impatti previsti per gli utenti #
Come descritto sopra deprecato usermedians
e conf_intervals
probabilmente avrà un impatto su pochi utenti. Coloro che ne risentiranno sono quasi certamente utenti avanzati che saranno in grado di adattarsi al cambiamento.
Deprecare l' sym
opzione può importare più utenti e dovrebbe essere fatto uno sforzo per raccogliere il feedback della comunità su questo.
Impatti previsti sulle biblioteche a valle #
Il codice sorgente (GitHub master al 17-10-2016) è stato ispezionato per seaborn e python-ggplot per vedere se queste modifiche avrebbero avuto un impatto sul loro utilizzo. Nessuno dei parametri nominati per la rimozione in questo MEP è utilizzato da Seaborn. Le API seaborn che utilizzano la funzione boxplot di matplotlib consentono all'utente di passare arbitrariamente **kwargs
all'API di matplotlib. Pertanto, gli utenti di Seaborn con moderne installazioni di matplotlib saranno in grado di sfruttare appieno tutte le nuove funzionalità aggiunte come risultato di questo MEP.
Python-ggplot ha implementato la propria funzione per disegnare boxplot. Pertanto, nessun impatto può derivare dall'attuazione di questo MEP.
Alternative #
Variazioni sul tema #
Questo eurodeputato può essere suddiviso in alcuni componenti debolmente accoppiati:
Consentire la funzione di trasformazione pre e post calcolo in
cbook.boxplot_stats
Esponendo tale trasformazione
Axes.boxplot
nell'APIRimozione delle opzioni statistiche ridondanti in
Axes.boxplot
Spostamento dell'elaborazione di tutti i parametri di stile da
Axes.boxplot
aAxes.bxp
.
Con questo approccio, #2 dipende e #1, e #4 dipende da #3.
Ci sono due possibili approcci al n. 2. Il primo e più diretto sarebbe rispecchiare i parametri
new transform_in
e di in e passarli direttamente.transform_out
cbook.boxplot_stats
Axes.boxplot
Il secondo approccio sarebbe aggiungere statfxn
e statfxn_args
parametri a Axes.boxplot
. In questa implementazione, il valore predefinito di statfxn
sarebbe cbook.boxplot_stats
, ma gli utenti potrebbero passare la propria funzione. Quindi transform_in
e transform_out
verrebbero quindi passati come elementi del statfxn_args
parametro.
def boxplot_stats(data, ..., transform_in=None, transform_out=None):
if transform_in is None:
transform_in = lambda x: x
if transform_out is None:
transform_out = lambda x: x
output = []
for _d in data:
d = transform_in(_d)
stat_dict = do_stats(d)
for key, value in stat_dict.item():
if key != 'label':
stat_dict[key] = transform_out(value)
output.append(d)
return output
class Axes(...):
def boxplot_option1(data, ..., transform_in=None, transform_out=None):
stats = cbook.boxplot_stats(data, ...,
transform_in=transform_in,
transform_out=transform_out)
return self.bxp(stats, ...)
def boxplot_option2(data, ..., statfxn=None, **statopts):
if statfxn is None:
statfxn = boxplot_stats
stats = statfxn(data, **statopts)
return self.bxp(stats, ...)
Entrambi i casi consentirebbero agli utenti di eseguire le seguenti operazioni:
fig, ax1 = plt.subplots()
artists1 = ax1.boxplot_optionX(data, transform_in=np.log,
transform_out=np.exp)
Ma l'opzione due consente a un utente di scrivere una funzione statistica completamente personalizzata (ad esempio, my_box_stats
) con intervalli di confidenza BCA fantasiosi e baffi impostati in modo diverso a seconda di alcuni attributi dei dati.
Questo è disponibile sotto l'API corrente:
fig, ax1 = plt.subplots()
my_stats = my_box_stats(data, bootstrap_method='BCA',
whisker_method='dynamic')
ax1.bxp(my_stats)
E sarebbe più conciso con l'opzione due
fig, ax = plt.subplots()
statopts = dict(transform_in=np.log, transform_out=np.exp)
ax.boxplot(data, ..., **statopts)
Gli utenti possono anche passare la propria funzione per calcolare le statistiche:
fig, ax1 = plt.subplots()
ax1.boxplot(data, statfxn=my_box_stats, bootstrap_method='BCA',
whisker_method='dynamic')
Dagli esempi precedenti, l'opzione due sembra avere solo un vantaggio marginale, ma nel contesto delle librerie a valle come seaborn, il suo vantaggio è più evidente in quanto sarebbe possibile quanto segue senza alcuna patch per seaborn:
import seaborn
tips = seaborn.load_data('tips')
g = seaborn.factorplot(x="day", y="total_bill", hue="sex", data=tips,
kind='box', palette="PRGn", shownotches=True,
statfxn=my_box_stats, bootstrap_method='BCA',
whisker_method='dynamic')
Questo tipo di flessibilità era l'intenzione alla base della suddivisione dell'API boxplot complessiva nelle tre funzioni attuali. In pratica, tuttavia, le librerie a valle come Seaborn supportano versioni di matplotlib che risalgono a ben prima della scissione. Pertanto, l'aggiunta di un po' più di flessibilità a
Axes.boxplot
potrebbe esporre tutte le funzionalità agli utenti delle librerie downstream con l'installazione moderna di matplotlib senza l'intervento dei manutentori delle librerie downstream.
Fare di meno #
Un'altra ovvia alternativa sarebbe quella di omettere la funzionalità di trasformazione pre e post computazione aggiunta in cbook.boxplot_stats
e
Axes.boxplot
e rimuovere semplicemente i parametri statistici e di stile ridondanti come descritto sopra.
Non fare niente #
Come per molte cose nella vita, non fare nulla è un'opzione qui. Ciò significa che sosteniamo semplicemente che gli utenti e le biblioteche a valle traggano vantaggio dalla divisione tra cbook.boxplot_stats
e Axes.bxp
lasciamo che decidano come fornire un'interfaccia a questo.