MEP28: Komplexität aus Axes.boxplot entfernen #
Status #
Diskussion
Branches und Pull-Requests #
Im Folgenden sind alle offenen PRs oder Zweige aufgeführt, die sich auf dieses MdEP beziehen:
Verwerfen Sie redundante statistische Kwargs in
Axes.boxplot
: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecationsVerwerfen Sie redundante Stiloptionen in
Axes.boxplot
: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecationsÜbergeben von 2D-NumPy-Arrays als Eingabe verwerfen: Keine
Fügen Sie Vor- und Nachbearbeitungsoptionen hinzu
cbook.boxplot_stats
: https://github.com/phobson/matplotlib/tree/boxplot-stat-transformscbook.boxplot_stats
Aufdecken durch KwargsAxes.boxplot
: KeineEntfernen Sie redundante statistische Kwargs in
Axes.boxplot
: KeineEntfernen Sie überflüssige Stiloptionen in
Axes.boxplot
: KeineVerbleibende Punkte, die sich aus der Diskussion ergeben: Keine
Zusammenfassung #
In den letzten Versionen wurde die Axes.boxplot
Methode immer komplexer, um vollständig anpassbares Künstler-Styling und statistische Berechnungen zu unterstützen. Dies führte zu Axes.boxplot
einer Aufspaltung in mehrere Teile. Die Statistiken, die zum Zeichnen eines Boxplots benötigt werden, werden berechnet
cbook.boxplot_stats
, während die eigentlichen Künstler von gezeichnet werden Axes.bxp
. Die ursprüngliche Methode Axes.boxplot
bleibt die öffentlichste API, die die Übergabe der vom Benutzer bereitgestellten Daten an cbook.boxplot_stats
, die Eingabe der Ergebnisse an Axes.bxp
, und die Vorverarbeitung von Stilinformationen für jede Facette der Boxplot-Diagramme übernimmt.
Dieses MEP wird einen Weg nach vorne skizzieren, um die zusätzliche Komplexität rückgängig zu machen und die API zu vereinfachen und gleichzeitig eine angemessene Abwärtskompatibilität aufrechtzuerhalten.
Detaillierte Beschreibung #
Derzeit Axes.boxplot
akzeptiert die Methode Parameter, die es den Benutzern ermöglichen, Mediane und Konfidenzintervalle für jede Box anzugeben, die im Diagramm gezeichnet wird. Diese wurden bereitgestellt, damit fortgeschrittene Benutzer Statistiken bereitstellen konnten, die auf andere Weise als die einfache Methode von matplotlib berechnet wurden. Die Verarbeitung dieser Eingabe erfordert jedoch eine komplexe Logik, um sicherzustellen, dass die Formen der Datenstruktur mit dem übereinstimmen, was gezeichnet werden muss. Im Moment enthält diese Logik 9 separate if/else-Anweisungen, die bis zu 5 Ebenen tief mit einer for-Schleife verschachtelt sind, und kann bis zu 2 Fehler auslösen. Diese Parameter wurden vor der Erstellung der Axes.bxp
Methode hinzugefügt, die Boxplots aus einer Liste von Wörterbüchern zeichnet, die die relevanten Statistiken enthalten. Matplotlib bietet auch eine Funktion, die diese Statistiken über berechnetcbook.boxplot_stats
. Beachten Sie, dass fortgeschrittene Benutzer jetzt entweder a) ihre eigene Funktion schreiben können, um die von benötigten Statistiken zu berechnen
Axes.bxp
, oder b) die von zurückgegebene Ausgabe ändern cbook.boxplots_stats
können, um die Position der Künstler der Plots vollständig anzupassen. Mit dieser Flexibilität bleiben die Parameter zur manuellen Angabe nur der Mediane und ihrer Konfidenzintervalle für die Abwärtskompatibilität erhalten.
Ungefähr zur gleichen Zeit, als die beiden Rollen von für die Berechnung und für das Zeichnen Axes.boxplot
aufgeteilt
wurden, wurden sowohl
und geschrieben, um Parameter zu akzeptieren, die das Zeichnen aller Komponenten der Boxplots einzeln umschalten, als auch Parameter, die den Stil dieser Künstler individuell konfigurieren. Um die Abwärtskompatibilität zu wahren, wurde der Parameter (der zuvor verwendet wurde, um das Symbol der Flieger anzugeben) beibehalten. Dieser Parameter selbst erfordert eine ziemlich komplexe Logik, um die Parameter mit dem neueren Parameter im durch angegebenen Standardstil abzugleichen .cbook.boxplot_stats
Axes.bxp
Axes.boxplot
Axes.bxp
sym
sym
flierprops
matplotlibrc
Dieses MEP versucht, die Erstellung von Boxplots für Anfänger und Fortgeschrittene gleichermaßen zu vereinfachen. Wichtig ist, dass die hier vorgeschlagenen Änderungen auch für nachgelagerte Pakete wie Seaborn verfügbar sein werden, da Seaborn Benutzern auf intelligente Weise ermöglicht, beliebige Wörterbücher von Parametern über die Seaborn-API an die zugrunde liegenden Matplotlib-Funktionen zu übergeben.
Dies wird auf folgende Weise erreicht:
cbook.boxplot_stats
wird so modifiziert, dass Transformationsfunktionen vor und nach der Berechnung übergeben werden können (z. B.np.log
undnp.exp
für lognormalverteilte Daten)
Axes.boxplot
wird modifiziert, um sie auch zu akzeptieren und naiv weiterzuleitencbook.boxplots_stats
(Alt: übergibt die stat-Funktion und ein Diktat ihrer optionalen Parameter).Veraltete Parameter von
Axes.boxplot
werden als veraltet markiert und später entfernt.
Wichtigkeit #
Da die Grenzen der Whisker arithmetisch berechnet werden, gibt es eine implizite Annahme der Normalität in Box- und Whisker-Plots. Dies betrifft vor allem, welche Datenpunkte als Ausreißer klassifiziert werden.
Durch das Zulassen von Transformationen der Daten und der zum Zeichnen von Boxplots verwendeten Ergebnisse können Benutzer diese Annahme ablehnen, wenn bekannt ist, dass die Daten nicht in eine Normalverteilung passen.
Nachfolgend finden Sie ein Beispiel dafür, wie Axes.boxplot
Ausreißer von lognormalen Daten in Abhängigkeit von diesen Transformationstypen unterschiedlich klassifiziert werden.
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()
Implementierung #
Transformationsfunktionen an cbook.boxplots_stats
# übergeben
Dieser Abgeordnete schlägt vor, dass zwei Parameter (z. B. transform_in
und
) transform_out
der Cookbook-Funktion hinzugefügt werden, die die Statistiken für die Boxplot-Funktion berechnet. Dies sind optionale Nur-Schlüsselwort-Argumente und können einfach als No-Op gesetzt werden, wenn sie vom Benutzer weggelassen werden. Die Funktion wird auf die Daten angewendet, während die Funktion jede Teilmenge der an sie übergebenen Daten durchläuft.Nachdem die Liste der Statistikwörterbücher berechnet
wurde, wird die Funktion auf jeden Wert in den Wörterbüchern angewendet.lambda x: x
transform_in
boxplot_stats
transform_out
Diese Transformationen können dann der Aufrufsignatur hinzugefügt werden
, ohne dass dies Axes.boxplot
die Komplexität dieser Methode beeinflusst. Das liegt daran, dass sie direkt an übergeben werden können cbook.boxplot_stats
. Alternativ Axes.boxplot
könnte modifiziert werden, um eine optionale statistische Funktion kwarg und ein Verzeichnis von Parametern zu akzeptieren, die direkt an sie übergeben werden.
An diesem Punkt der Implementierung hätten Benutzer und externe Bibliotheken wie Seaborn die vollständige Kontrolle über die Axes.boxplot
Methode. Noch wichtiger ist, dass Seaborn zumindest keine Änderungen an seiner API erfordern würde, damit Benutzer diese neuen Optionen nutzen können.
Vereinfachungen der Axes.boxplot
API und anderer Funktionen #
Die Vereinfachung der Boxplot-Methode besteht hauptsächlich darin, die redundanten Parameter zu verwerfen und dann zu entfernen. Optional wäre ein nächster Schritt die Behebung geringfügiger terminologischer Inkonsistenzen zwischen Axes.boxplot
und Axes.bxp
.
Zu den veralteten und zu entfernenden Parametern gehören:
usermedians
- verarbeitet von 10 SLOC, 3if
Blöcken, einerfor
Schleife
conf_intervals
- behandelt von 15 SLOC, 6if
Blöcken, einerfor
Schleife
sym
- verarbeitet von 12 SLOC, 4if
Blöcke
Durch das Entfernen der sym
Option kann der gesamte Code zur Handhabung der verbleibenden Stilparameter nach verschoben werden Axes.bxp
. Dies beseitigt keine Komplexität, verstärkt jedoch das Prinzip der Einzelverantwortung zwischen Axes.bxp
, cbook.boxplot_stats
, und Axes.boxplot
.
Außerdem könnte der notch
Parameter so umbenannt shownotches
werden, dass er konsistent mit ist Axes.bxp
. Diese Art der Bereinigung könnte noch einen Schritt weiter gehen und die whis
, bootstrap
, autorange
könnten in die kwargs gerollt werden, die an den neuen statfxn
Parameter übergeben werden.
Abwärtskompatibilität #
Die Implementierung dieses MEP würde schließlich zu einer rückwärtsinkompatiblen Ablehnung und anschließenden Entfernung der Schlüsselwortparameter
usermedians
, conf_intervals
, und führen sym
. Flüchtige Suchen auf GitHub zeigten, dass usermedians
, conf_intervals
von wenigen Benutzern verwendet werden, die alle sehr gute Kenntnisse von matplotlib zu haben scheinen. Ein robuster Verfallszyklus sollte diesen Benutzern ausreichend Zeit geben, auf eine neue API zu migrieren.
Die Ablehnung von sym
kann jedoch eine viel größere Reichweite in der Matplotlib-Benutzerbasis haben.
Zeitplan #
Ein beschleunigter Zeitplan könnte wie folgt aussehen:
v2.0.1 füge Transformationen zu hinzu
cbook.boxplots_stats
, exponiere inAxes.boxplot
v2.1.0 Anfängliche Verwerfungen und die Verwendung von 2D-NumPy-Arrays als Eingabe
Verwenden von 2D-NumPy-Arrays als Eingabe. Die Semantik rund um 2D-Arrays ist im Allgemeinen verwirrend.
usermedians
,conf_intervals
,sym
Parameter
v2.2.0
usermedians
,conf_intervals
,sym
Parameter entfernenverwerfen
notch
zugunsten vonshownotches
konsistent mit anderen Parametern undAxes.bxp
- v2.3.0
notch
Parameter entfernenmove all style and artist toggling logic to these
Axes.bxp
isAxes.boxplot
wenig mehr als ein Vermittler zwischenAxes.bxp
undcbook.boxplots_stats
Voraussichtliche Auswirkungen auf Benutzer #
Wie oben beschrieben veraltet usermedians
und conf_intervals
wird wahrscheinlich nur wenige Benutzer betreffen. Diejenigen, die betroffen sein werden, sind mit ziemlicher Sicherheit fortgeschrittene Benutzer, die sich an die Änderung anpassen können.
Das Ablehnen der sym
Option kann mehr Benutzer importieren und es sollten Anstrengungen unternommen werden, Community-Feedback dazu zu sammeln.
Erwartete Auswirkungen auf nachgelagerte Bibliotheken #
Der Quellcode (GitHub-Master vom 17.10.2016) wurde für seaborn und python-ggplot untersucht, um festzustellen, ob diese Änderungen ihre Verwendung beeinträchtigen würden. Keiner der in diesem MEP zur Entfernung vorgeschlagenen Parameter wird von Seaborn verwendet. Die Seaborn-APIs, die die Boxplot-Funktion von matplotlib verwenden, ermöglichen es Benutzern, willkürlich **kwargs
zur API von matplotlib zu gelangen. Daher können seegeborene Benutzer mit modernen Matplotlib-Installationen alle neuen Funktionen, die als Ergebnis dieses MEP hinzugefügt wurden, voll ausnutzen.
Python-ggplot hat eine eigene Funktion zum Zeichnen von Boxplots implementiert. Daher kann es durch die Umsetzung dieses MEP zu keinen Auswirkungen kommen.
Alternativen #
Variationen über das Thema #
Dieses MEP kann in einige lose gekoppelte Komponenten unterteilt werden:
Transformationsfunktion vor und nach der Berechnung zulassen
cbook.boxplot_stats
Verfügbarmachen dieser Transformation in der
Axes.boxplot
APIEntfernen redundanter statistischer Optionen in
Axes.boxplot
Verschieben der Verarbeitung aller Styling-Parameter von
Axes.boxplot
aufAxes.bxp
.
Bei diesem Ansatz hängt #2 von #1 und #4 von #3 ab.
Es gibt zwei mögliche Ansätze für #2. Die erste und direkteste wäre, die neuen transform_in
und transform_out
Parameter von
cbook.boxplot_stats
in zu spiegeln Axes.boxplot
und direkt zu übergeben.
Der zweite Ansatz wäre das Hinzufügen von statfxn
und statfxn_args
Parametern zu Axes.boxplot
. Bei dieser Implementierung statfxn
wäre der Standardwert von cbook.boxplot_stats
, aber Benutzer könnten ihre eigene Funktion übergeben. Dann würden transform_in
und transform_out
dann als Elemente des statfxn_args
Parameters übergeben.
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, ...)
In beiden Fällen könnten Benutzer Folgendes tun:
fig, ax1 = plt.subplots()
artists1 = ax1.boxplot_optionX(data, transform_in=np.log,
transform_out=np.exp)
Aber Option Zwei lässt einen Benutzer eine vollständig benutzerdefinierte Statistikfunktion (z. B. my_box_stats
) mit ausgefallenen BCA-Konfidenzintervallen schreiben und die Schnurrhaare werden je nach Attribut der Daten unterschiedlich gesetzt.
Dies ist unter der aktuellen API verfügbar:
fig, ax1 = plt.subplots()
my_stats = my_box_stats(data, bootstrap_method='BCA',
whisker_method='dynamic')
ax1.bxp(my_stats)
Und wäre prägnanter mit Option Zwei
fig, ax = plt.subplots()
statopts = dict(transform_in=np.log, transform_out=np.exp)
ax.boxplot(data, ..., **statopts)
Benutzer könnten auch ihre eigene Funktion übergeben, um die Statistiken zu berechnen:
fig, ax1 = plt.subplots()
ax1.boxplot(data, statfxn=my_box_stats, bootstrap_method='BCA',
whisker_method='dynamic')
Aus den obigen Beispielen scheint Option Zwei nur einen marginalen Nutzen zu haben, aber im Kontext nachgelagerter Bibliotheken wie Seaborn ist ihr Vorteil offensichtlicher, da Folgendes ohne Patches für Seaborn möglich wäre:
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')
Diese Art von Flexibilität war die Absicht hinter der Aufteilung der gesamten Boxplot-API in die aktuellen drei Funktionen. In der Praxis unterstützen jedoch nachgelagerte Bibliotheken wie Seaborn Versionen von Matplotlib, die weit vor der Trennung zurückreichen. Wenn Sie also nur ein bisschen mehr Flexibilität hinzufügen,
Axes.boxplot
könnte die gesamte Funktionalität Benutzern der Downstream-Bibliotheken mit moderner matplotlib-Installation ohne Eingriff der Downstream-Bibliotheksbetreuer zur Verfügung gestellt werden.
Weniger tun #
Eine andere offensichtliche Alternative wäre, die hinzugefügte Vor- und Nachberechnungstransformationsfunktionalität in cbook.boxplot_stats
und
wegzulassen Axes.boxplot
und einfach die redundanten Statistik- und Stilparameter zu entfernen, wie oben beschrieben.
Nichts tun #
Wie bei vielen Dingen im Leben ist auch hier Nichtstun eine Option. Das bedeutet, dass wir einfach dafür plädieren, dass Benutzer und nachgelagerte Bibliotheken die Trennung zwischen nutzen cbook.boxplot_stats
und Axes.bxp
sie entscheiden lassen, wie sie eine Schnittstelle dafür bereitstellen.