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:

  1. Verwerfen Sie redundante statistische Kwargs in Axes.boxplot: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations

  2. Verwerfen Sie redundante Stiloptionen in Axes.boxplot: https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations

  3. Übergeben von 2D-NumPy-Arrays als Eingabe verwerfen: Keine

  4. Fügen Sie Vor- und Nachbearbeitungsoptionen hinzu cbook.boxplot_stats: https://github.com/phobson/matplotlib/tree/boxplot-stat-transforms

  5. cbook.boxplot_statsAufdecken durch Kwargs Axes.boxplot: Keine

  6. Entfernen Sie redundante statistische Kwargs in Axes.boxplot: Keine

  7. Entfernen Sie überflüssige Stiloptionen in Axes.boxplot: Keine

  8. Verbleibende Punkte, die sich aus der Diskussion ergeben: Keine

Zusammenfassung #

In den letzten Versionen wurde die Axes.boxplotMethode immer komplexer, um vollständig anpassbares Künstler-Styling und statistische Berechnungen zu unterstützen. Dies führte zu Axes.boxploteiner 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.boxplotbleibt 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.boxplotakzeptiert 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.bxpMethode 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.boxplotaufgeteilt 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_statsAxes.bxpAxes.boxplotAxes.bxpsymsymflierpropsmatplotlibrc

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:

  1. cbook.boxplot_statswird so modifiziert, dass Transformationsfunktionen vor und nach der Berechnung übergeben werden können (z. B. np.log und np.expfür lognormalverteilte Daten)

  2. Axes.boxplotwird modifiziert, um sie auch zu akzeptieren und naiv weiterzuleiten cbook.boxplots_stats(Alt: übergibt die stat-Funktion und ein Diktat ihrer optionalen Parameter).

  3. Veraltete Parameter von Axes.boxplotwerden 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.boxplotAusreiß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()

( Quellcode , png )

../../_images/MEP28-1.png

Implementierung #

Transformationsfunktionen an cbook.boxplots_stats# übergeben

Dieser Abgeordnete schlägt vor, dass zwei Parameter (z. B. transform_inund ) transform_outder 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: xtransform_inboxplot_statstransform_out

Diese Transformationen können dann der Aufrufsignatur hinzugefügt werden , ohne dass dies Axes.boxplotdie Komplexität dieser Methode beeinflusst. Das liegt daran, dass sie direkt an übergeben werden können cbook.boxplot_stats. Alternativ Axes.boxplotkö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.boxplotMethode. 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.boxplotAPI 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:

  1. usermedians- verarbeitet von 10 SLOC, 3 ifBlöcken, einer forSchleife

  2. conf_intervals- behandelt von 15 SLOC, 6 ifBlöcken, einer forSchleife

  3. sym- verarbeitet von 12 SLOC, 4 ifBlöcke

Durch das Entfernen der symOption 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 notchParameter 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, autorangekönnten in die kwargs gerollt werden, die an den neuen statfxnParameter ü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_intervalsvon 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 symkann jedoch eine viel größere Reichweite in der Matplotlib-Benutzerbasis haben.

Zeitplan #

Ein beschleunigter Zeitplan könnte wie folgt aussehen:

  1. v2.0.1 füge Transformationen zu hinzu cbook.boxplots_stats, exponiere inAxes.boxplot

  2. v2.1.0 Anfängliche Verwerfungen und die Verwendung von 2D-NumPy-Arrays als Eingabe

    1. Verwenden von 2D-NumPy-Arrays als Eingabe. Die Semantik rund um 2D-Arrays ist im Allgemeinen verwirrend.

    2. usermedians, conf_intervals, symParameter

  3. v2.2.0

    1. usermedians, conf_intervals, symParameter entfernen

    2. verwerfen notchzugunsten von shownotcheskonsistent mit anderen Parametern undAxes.bxp

  4. v2.3.0
    1. notchParameter entfernen

    2. move all style and artist toggling logic to these Axes.bxpis Axes.boxplot wenig mehr als ein Vermittler zwischen Axes.bxpundcbook.boxplots_stats

Voraussichtliche Auswirkungen auf Benutzer #

Wie oben beschrieben veraltet usermediansund 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 symOption 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 **kwargszur 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:

  1. Transformationsfunktion vor und nach der Berechnung zulassencbook.boxplot_stats

  2. Verfügbarmachen dieser Transformation in der Axes.boxplotAPI

  3. Entfernen redundanter statistischer Optionen inAxes.boxplot

  4. Verschieben der Verarbeitung aller Styling-Parameter von Axes.boxplotauf Axes.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_inund transform_outParameter von cbook.boxplot_statsin zu spiegeln Axes.boxplotund direkt zu übergeben.

Der zweite Ansatz wäre das Hinzufügen von statfxnund statfxn_args Parametern zu Axes.boxplot. Bei dieser Implementierung statfxnwäre der Standardwert von cbook.boxplot_stats, aber Benutzer könnten ihre eigene Funktion übergeben. Dann würden transform_inund transform_outdann als Elemente des statfxn_argsParameters ü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.boxplotkö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_statsund wegzulassen Axes.boxplotund 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_statsund Axes.bxpsie entscheiden lassen, wie sie eine Schnittstelle dafür bereitstellen.