MEP28 : Supprimer la complexité de Axes.boxplot #

Statut #

Discussion

Branches et demandes d'extraction #

La liste suivante répertorie tous les PR ouverts ou les succursales liées à ce MEP :

  1. Déprécier les kwargs statistiques redondants dansAxes.boxplot : https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations

  2. Déprécier les options de style redondantes dansAxes.boxplot : https://github.com/phobson/matplotlib/tree/MEP28-initial-deprecations

  3. Déprécier les passages de tableaux NumPy 2D en entrée : aucun

  4. Ajoutez des options de pré- et post-traitement àcbook.boxplot_stats : https://github.com/phobson/matplotlib/tree/boxplot-stat-transforms

  5. Exposer cbook.boxplot_statsà travers Axes.boxplotles kwargs : aucun

  6. Supprimer les kwargs statistiques redondants dans Axes.boxplot: Aucun

  7. Supprimer les options de style redondantes dans Axes.boxplot: Aucun

  8. Autres éléments soulevés lors de la discussion : aucun

Résumé #

Au cours des dernières versions, la Axes.boxplotméthode est devenue plus complexe pour prendre en charge un style d'artiste et un calcul statistique entièrement personnalisables. Cela a conduit à Axes.boxplotêtre divisé en plusieurs parties. Les statistiques nécessaires pour dessiner une boîte à moustaches sont calculées dans cbook.boxplot_stats, tandis que les artistes réels sont dessinés par Axes.bxp. La méthode d'origine Axes.boxplotreste l'API la plus publique qui gère la transmission des données fournies par l'utilisateur à cbook.boxplot_stats, l'envoi des résultats à Axes.bxpet le prétraitement des informations de style pour chaque facette des tracés de boîtes à moustaches.

Ce MEP décrira une voie à suivre pour annuler la complexité supplémentaire et simplifier l'API tout en maintenant une rétrocompatibilité raisonnable.

Descriptif détaillé #

Actuellement, la Axes.boxplotméthode accepte des paramètres qui permettent aux utilisateurs de spécifier des médianes et des intervalles de confiance pour chaque boîte qui sera dessinée dans le tracé. Celles-ci ont été fournies afin que les utilisateurs avancés puissent fournir des statistiques calculées d'une manière différente de la méthode simple fournie par matplotlib. Cependant, la gestion de cette entrée nécessite une logique complexe pour s'assurer que les formes de la structure de données correspondent à ce qui doit être dessiné. Pour le moment, cette logique contient 9 instructions if/else séparées imbriquées jusqu'à 5 niveaux de profondeur avec une boucle for et peut générer jusqu'à 2 erreurs. Ces paramètres ont été ajoutés avant la création de la Axes.bxpméthode, qui dessine des boîtes à moustaches à partir d'une liste de dictionnaires contenant les statistiques pertinentes. Matplotlib fournit également une fonction qui calcule ces statistiques viacbook.boxplot_stats. Notez que les utilisateurs avancés peuvent désormais a) écrire leur propre fonction pour calculer les statistiques requises par Axes.bxp, ou b) modifier la sortie renvoyée par cbook.boxplots_stats pour personnaliser entièrement la position des artistes des tracés. Avec cette flexibilité, les paramètres permettant de spécifier manuellement uniquement les médianes et leurs intervalles de confiance restent pour une rétrocompatibilité.

À peu près au même moment où les deux rôles de Axes.boxplotont été divisés en cbook.boxplot_statscalcul et Axes.bxpen dessin, les deux Axes.boxplotet Axes.bxpont été écrits pour accepter des paramètres qui basculent individuellement le dessin de tous les composants des boîtes à moustaches, et des paramètres qui configurent individuellement le style de ces artistes. Cependant, pour maintenir la rétrocompatibilité, le symparamètre (précédemment utilisé pour spécifier le symbole des dépliants) a été conservé. Ce paramètre lui-même nécessite une logique assez complexe pour réconcilier les symparamètres avec le nouveau flierpropsparamètre au style par défaut spécifié par matplotlibrc.

Ce MEP vise à simplifier considérablement la création de boîtes à moustaches pour les utilisateurs novices et avancés. Il est important de noter que les modifications proposées ici seront également disponibles pour les packages en aval comme seaborn, car seaborn permet intelligemment aux utilisateurs de transmettre des dictionnaires arbitraires de paramètres via l'API seaborn aux fonctions matplotlib sous-jacentes.

Ceci sera réalisé de la manière suivante :

  1. cbook.boxplot_statssera modifié pour permettre aux fonctions de transformation pré- et post-calcul d'être transmises (par exemple, np.log et np.exppour les données distribuées log-normalement)

  2. Axes.boxplotsera modifié pour les accepter également et les transmettre naïvement à cbook.boxplots_stats(Alt : transmettre la fonction stat et un dict de ses paramètres optionnels).

  3. Les paramètres obsolètes de Axes.boxplotseront obsolètes et supprimés ultérieurement.

Importance #

Étant donné que les limites des moustaches sont calculées arithmétiquement, il existe une hypothèse implicite de normalité dans les diagrammes en boîte et moustaches. Cela affecte principalement les points de données classés comme valeurs aberrantes.

Autoriser les transformations des données et des résultats utilisés pour dessiner des boîtes à moustaches permettra aux utilisateurs de refuser cette hypothèse si les données sont connues pour ne pas correspondre à une distribution normale.

Vous trouverez ci-dessous un exemple de la manière dont Axes.boxplotles valeurs aberrantes des données log-normales sont classées différemment en fonction de ces types de transformations.

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()

( Code source , png )

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

Mise en œuvre #

Passer des fonctions de transformation à cbook.boxplots_stats#

Ce MEP propose que deux paramètres (par exemple, transform_inet transform_outsoient ajoutés à la fonction de livre de recettes qui calcule les statistiques pour la fonction de boîte à moustaches. Ce seront des arguments facultatifs de mot-clé uniquement et peuvent facilement être définis comme non-op lorsqu'ils sont omis par l'utilisateur. La fonction sera appliquée aux données au fur et à mesure que la fonction parcourt chaque sous-ensemble des données qui lui sont transmises. Une fois la liste des dictionnaires de statistiques calculée, la fonction est appliquée à chaque valeur des dictionnaires.lambda x: xtransform_inboxplot_statstransform_out

Ces transformations peuvent ensuite être ajoutées à la signature d'appel Axes.boxplotavec peu d'impact sur la complexité de cette méthode. C'est parce qu'ils peuvent être directement passés à cbook.boxplot_stats. Alternativement, Axes.boxplotpourrait être modifié pour accepter une fonction statistique facultative kwarg et un dictionnaire de paramètres à lui transmettre directement.

À ce stade de l'implémentation, les utilisateurs et les bibliothèques externes comme seaborn auraient un contrôle total via la Axes.boxplotméthode. Plus important encore, à tout le moins, seaborn ne nécessiterait aucune modification de son API pour permettre aux utilisateurs de profiter de ces nouvelles options.

Simplifications de l' Axes.boxplotAPI et d'autres fonctions #

La simplification de la méthode du boxplot consiste principalement à déprécier puis à supprimer les paramètres redondants. Facultativement, une prochaine étape consisterait à rectifier les incohérences terminologiques mineures entre Axes.boxplot et Axes.bxp.

Les paramètres à déconseiller et à supprimer incluent :

  1. usermedians- traité par 10 SLOC, 3 ifblocs, une forboucle

  2. conf_intervals- géré par 15 SLOC, 6 ifblocs, une forboucle

  3. sym- traité par 12 SLOC, 4 ifblocs

La suppression de l' symoption permet de déplacer tout le code de gestion des paramètres de style restants vers Axes.bxp. Cela ne supprime aucune complexité, mais renforce le principe de responsabilité unique entre Axes.bxp, cbook.boxplot_statset Axes.boxplot.

De plus, le notchparamètre peut être renommé shownotches pour être cohérent avec Axes.bxp. Ce type de nettoyage pourrait être poussé plus loin et les whis, bootstrap, autorangepourraient être intégrés aux kwargs passés au nouveau statfxnparamètre.

Rétrocompatibilité #

La mise en œuvre de ce MEP entraînerait éventuellement l'obsolescence rétrocompatible, puis la suppression des paramètres de mot-clé usermedians, conf_intervalset sym. Des recherches rapides sur GitHub ont indiqué que usermedians, conf_intervalssont utilisés par quelques utilisateurs, qui semblent tous avoir une très bonne connaissance de matplotlib. Un cycle de dépréciation robuste devrait laisser suffisamment de temps à ces utilisateurs pour migrer vers une nouvelle API.

symCependant, la dépréciation de peut avoir une portée beaucoup plus large dans la base d'utilisateurs de matplotlib .

Horaire #

Une chronologie accélérée pourrait ressembler à ceci :

  1. v2.0.1 ajouter des transformations à cbook.boxplots_stats, exposer dansAxes.boxplot

  2. v2.1.0 Dépréciations initiales et utilisation de tableaux NumPy 2D en entrée

    1. Utilisation de tableaux NumPy 2D en entrée. La sémantique autour des tableaux 2D est généralement déroutante.

    2. usermedians, conf_intervals, symparamètres

  3. v2.2.0

    1. supprimer les paramètres usermedians, conf_intervals,sym

    2. déprécier notchen faveur de shownotchespour être cohérent avec d'autres paramètres etAxes.bxp

  4. v2.3.0
    1. supprimer le notchparamètre

    2. déplacer tout le style et la logique de basculement de l'artiste vers Axes.bxptel Axes.boxplot n'est guère plus qu'un courtier entre Axes.bxpetcbook.boxplots_stats

Impacts anticipés sur les utilisateurs #

Comme décrit ci-dessus, il est obsolète usermedianset conf_intervals aura probablement un impact sur quelques utilisateurs. Ceux qui seront impactés sont presque certainement des utilisateurs avancés qui sauront s'adapter au changement.

L'abandon de l' symoption peut importer plus d'utilisateurs et des efforts doivent être faits pour recueillir les commentaires de la communauté à ce sujet.

Impacts anticipés sur les bibliothèques en aval #

Le code source (maître GitHub au 17/10/2016) a été inspecté pour seaborn et python-ggplot pour voir si ces changements auraient un impact sur leur utilisation. Aucun des paramètres désignés pour suppression dans ce MEP n'est utilisé par seaborn. Les API Seaborn qui utilisent la fonction boxplot de matplotlib permettent à l'utilisateur de passer arbitrairement **kwargsà l'API de matplotlib. Ainsi, les utilisateurs marins disposant d'installations matplotlib modernes pourront profiter pleinement de toutes les nouvelles fonctionnalités ajoutées à la suite de ce MEP.

Python-ggplot a implémenté sa propre fonction pour dessiner des boxplots. Par conséquent, aucun impact ne peut lui arriver à la suite de la mise en œuvre de ce PEM.

Alternatives #

Variations sur le thème #

Ce MEP peut être divisé en quelques composants faiblement couplés :

  1. Permettre la fonction de transformation pré- et post-calcul danscbook.boxplot_stats

  2. Exposer cette transformation dans l' Axes.boxplotAPI

  3. Suppression des options statistiques redondantes dansAxes.boxplot

  4. Déplacement de tous les paramètres de style de traitement de Axes.boxplotà Axes.bxp.

Avec cette approche, #2 dépend et #1, et #4 dépend de #3.

Il y a deux approches possibles pour #2. La première et la plus directe serait de refléter les nouveaux paramètres de in transform_inet de les transmettre directement.transform_outcbook.boxplot_statsAxes.boxplot

La deuxième approche consisterait à ajouter statfxnet statfxn_args des paramètres à Axes.boxplot. Dans cette implémentation, la valeur par défaut de statfxnserait cbook.boxplot_stats, mais les utilisateurs pourraient transmettre leur propre fonction. Alors transform_inet transform_outseraient alors passés en tant qu'éléments du statfxn_argsparamètre.

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, ...)

Les deux cas permettraient aux utilisateurs d'effectuer les opérations suivantes :

fig, ax1 = plt.subplots()
artists1 = ax1.boxplot_optionX(data, transform_in=np.log,
                               transform_out=np.exp)

Mais la deuxième option permet à un utilisateur d'écrire une fonction statistique entièrement personnalisée (par exemple, my_box_stats) avec des intervalles de confiance BCA sophistiqués et des moustaches définies différemment en fonction de certains attributs des données.

Ceci est disponible sous l'API actuelle :

fig, ax1 = plt.subplots()
my_stats = my_box_stats(data, bootstrap_method='BCA',
                        whisker_method='dynamic')
ax1.bxp(my_stats)

Et serait plus concis avec l'option deux

fig, ax = plt.subplots()
statopts = dict(transform_in=np.log, transform_out=np.exp)
ax.boxplot(data, ..., **statopts)

Les utilisateurs peuvent également transmettre leur propre fonction pour calculer les statistiques :

fig, ax1 = plt.subplots()
ax1.boxplot(data, statfxn=my_box_stats, bootstrap_method='BCA',
            whisker_method='dynamic')

D'après les exemples ci-dessus, l'option 2 semble n'avoir qu'un avantage marginal, mais dans le contexte des bibliothèques en aval comme seaborn, son avantage est plus apparent car ce qui suit serait possible sans aucun correctif pour 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')

Ce type de flexibilité était l'intention derrière la division de l'API boxplot globale dans les trois fonctions actuelles. En pratique cependant, les bibliothèques en aval comme seaborn prennent en charge les versions de matplotlib datant bien avant la scission. Ainsi, ajouter un peu plus de flexibilité à Axes.boxplotpourrait exposer toutes les fonctionnalités aux utilisateurs des bibliothèques en aval avec une installation moderne de matplotlib sans intervention des responsables de la bibliothèque en aval.

Faire moins #

Une autre alternative évidente consisterait à omettre la fonctionnalité de transformation pré- et post-calcul ajoutée dans cbook.boxplot_statset Axes.boxplot, et à supprimer simplement les paramètres statistiques et de style redondants, comme décrit ci-dessus.

Ne rien faire #

Comme pour beaucoup de choses dans la vie, ne rien faire est une option ici. Cela signifie que nous préconisons simplement que les utilisateurs et les bibliothèques en aval profitent de la séparation entre cbook.boxplot_statset Axes.bxpet les laissent décider comment fournir une interface à cela.