Pandas: API : définir l'API pour les backends de traçage des pandas

Créé le 9 juin 2019  ·  44Commentaires  ·  Source: pandas-dev/pandas

Dans #26414, nous avons divisé le module de traçage pandas en un cadre de traçage général capable d'appeler différents backends et les backends matplotlib actuels. L'idée est que d'autres backends peuvent être implémentés de manière plus simple et être utilisés avec une API commune par les utilisateurs de pandas.

L'API définie par le backend matplotlib actuel inclut les objets listés ci-dessous, mais cette API peut probablement être simplifiée. Voici la liste des questions/propositions :

Méthodes non controversées à conserver dans l'API (elles fournissent la fonctionnalité Series.plot(kind='line') ...) :

  • Graphique linéaire
  • BarPlot
  • BarhPlot
  • HistPlot
  • BoxPlot
  • KdePlot
  • ZonePlot
  • PiePlot
  • Nuage de points
  • HexBinPlot

Fonctions de traçage fournies dans les pandas (par exemple pandas.plotting.andrews_curves(df) )

  • andrews_curves
  • autocorrelation_plot
  • bootstrap_plot
  • lag_plot
  • coordonnées_parallèles
  • radviz
  • scatter_matrix
  • table

Ceux-ci devraient-ils faire partie de l'API et d'autres backends devraient-ils également les implémenter ? Serait-il judicieux de convertir au format .plot (par exemple DataFrame.plot(kind='autocorrelation') ...) ? Est-il judicieux de rester en dehors de l'API ou de passer à un module tiers ?

Méthodes redondantes qui peuvent éventuellement être supprimées :

  • hist_series
  • hist_frame
  • boîte à moustaches
  • boxplot_frame
  • boxplot_frame_groupby

Dans le cas de boxplot , nous avons actuellement plusieurs façons de générer un tracé (en appelant principalement le même code) :

  1. DataFrame.plot.boxplot()
  2. DataFrame.plot(kind='box')
  3. DataFrame.boxplot()
  4. pandas.plotting.boxplot(df)

Personnellement, je déprécierais le numéro 4, et pour le numéro 3, déprécier ou du moins ne pas exiger une méthode distincte boxplot_frame dans le backend, mais essayez de réutiliser BoxPlot (pour les commentaires numéro 3, même s'applique à hist ).

Pour boxplot_frame_groupby , vous n'avez pas vérifié en détail, mais vous ne savez pas si BoxPlot pourrait être réutilisé pour cela ?

Fonctions pour enregistrer les convertisseurs :

  • S'inscrire
  • se désinscrire

Cela a-t-il un sens pour d'autres backends ?

Obsolète dans pandas 0.23, à supprimer :

  • tsplot

Pour voir ce que chacune de ces fonctions fait en pratique, il peut être utile ce carnet de @liirusuk : https://github.com/python-sprints/pandas_plotting_library/blob/master/AllPlottingExamples.ipynb

CC : @pandas-dev/pandas-core @tacaswell , @jakevdp , @philippjfr , @PatrikHlobil

API Design Clean Needs Discussion Visualization

Commentaire le plus utile

Voici une implémentation basée sur les points d'entrée

diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
index 0610780ed..c8ac12901 100644
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -1532,8 +1532,10 @@ class PlotAccessor(PandasObject):

         return self(kind="hexbin", x=x, y=y, C=C, **kwargs)

+_backends = {}

-def _get_plot_backend(backend=None):
+
+def _get_plot_backend(backend="matplotlib"):
     """
     Return the plotting backend to use (e.g. `pandas.plotting._matplotlib`).

@@ -1546,7 +1548,14 @@ def _get_plot_backend(backend=None):
     The backend is imported lazily, as matplotlib is a soft dependency, and
     pandas can be used without it being installed.
     """
-    backend_str = backend or pandas.get_option("plotting.backend")
-    if backend_str == "matplotlib":
-        backend_str = "pandas.plotting._matplotlib"
-    return importlib.import_module(backend_str)
+    import pkg_resources  # slow import. Delay
+    if backend in _backends:
+        return _backends[backend]
+
+    for entry_point in pkg_resources.iter_entry_points("pandas_plotting_backends"):
+        _backends[entry_point.name] = entry_point.load()
+
+    try:
+        return _backends[backend]
+    except KeyError:
+        raise ValueError("No backend {}".format(backend))
diff --git a/setup.py b/setup.py
index 53e12da53..d2c6b18b8 100755
--- a/setup.py
+++ b/setup.py
@@ -830,5 +830,10 @@ setup(
             "hypothesis>=3.58",
         ]
     },
+    entry_points={
+        "pandas_plotting_backends": [
+            "matplotlib = pandas:plotting._matplotlib",
+        ],
+    },
     **setuptools_kwargs
 )

Je pense que c'est plutôt sympa. Les packages tiers modifieront leur setup.py (ou pyproject.toml) pour inclure quelque chose comme

entry_points={
    "pandas_plotting_backends": ["altair = pdvega._pandas_plotting_backend"]
}

J'aime que cela brise le couplage étroit entre le nommage et la mise en œuvre.

Tous les 44 commentaires

Je pense garder des choses comme l'autocorrélation hors de l'API backend échangeable.

Je pense que nous avons laissé des choses comme df.boxplot et hist parce qu'elles ont un comportement légèrement différent de celui de l'API .plot. Je ne recommanderais pas de les intégrer à l'API backend.

Voici mon début sur une API backend proposée il y a quelques mois : https://github.com/TomAugspurger/pandas/commit/b07aba28a37b0291fd96a1f571848a7be2b6de8d

Je pense qu'il vaut la peine de mentionner qu'au moins hvplot (je n'ai pas vérifié le reste) fournit déjà les fonctions comme andrews_curves , scatter_matrix , lag_plot ,. ..

Peut-être que si nous ne voulons pas forcer tous les backends à les implémenter, nous pouvons vérifier si le backend sélectionné les implémente et utiliser par défaut les tracés matplotlib ?

J'ai supposé que boxplot et hist se comportaient exactement de la même manière, mais j'avais juste des raccourcis Series.hist() pour Series.plot.hist() . Le "raccourci" montre la grille de tracé, mais à part cela, je n'ai vu aucune différence.

IMO, la valeur principale de cette option est l'espace .plot noms

Si les utilisateurs veulent le tracé de la courbe d'Andrew de hvplot, ils doivent importer la fonction
à partir de hvplot et passez-y la trame de données.

Le dimanche 9 juin 2019 à 7h17, Marc Garcia [email protected] a écrit :

Je pense qu'il vaut la peine de mentionner qu'au moins hvplot (n'a pas vérifié le
rest) fournit déjà les fonctions comme andrews_curves,
scatter_matrix, lag_plot,...

Peut-être que si nous ne voulons pas forcer tous les backends à les implémenter, nous pouvons
vérifier si le backend sélectionné les implémente, et par défaut le
parcelles matplotlib ?

J'ai supposé que boxplot et hist se comportaient exactement de la même manière, mais j'avais juste
raccourcis Series.hist() pour Series.plot.hist(). Le "raccourci" montre le
grille de tracé, mais à part ça, je n'ai vu aucune différence.

-
Vous recevez ceci parce que vous faites partie d'une équipe qui a été mentionnée.
Répondez directement à cet e-mail, consultez-le sur GitHub
Https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOIRLJHBMXMXKK2IG2NDPZTYFPA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMV20YW2ZLOXDNGO500P
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AAKAOISDHL6H7PVOOJAQXELPZTYFPANCNFSM4HWIMEKQ
.

Je pense que cela a du sens, mais si nous faisons cela, je pense que nous devrions les déplacer vers pandas.plotting.matplotlib.andrews_curves , au lieu de pandas.plotting.andrews_curves .

@TomAugspurger Je dois vérifier plus en détail, mais je pense que l'API que vous avez implémentée dans https://github.com/TomAugspurger/pandas/commit/b07aba28a37b0291fd96a1f571848a7be2b6de8d est celle qui a le plus de sens. Je travaillerai dessus une fois que j'aurai fini #26753. Je vais également tester s'il est possible de déplacer andrews_curves , scatter_matrix ... vers la syntaxe .plot() , je pense que cela rendra les choses plus simples et plus faciles pour tout le monde (nous , bibliothèques tierces et utilisateurs).

Quelle est l'intention ici concernant les kwargs supplémentaires passés aux fonctions de traçage ? Des backends supplémentaires devraient-ils tenter de dupliquer la fonctionnalité de toutes les personnalisations de tracé de style matplotlib, ou devraient-ils autoriser la transmission de mots-clés correspondant à ceux utilisés par le backend particulier ?

La première option serait bien en théorie, mais nécessiterait que chaque backend de traçage non-matplotlib implémente essentiellement sa propre couche de conversion matplotlib avec une longue queue d'incompatibilités qui ne seraient pratiquement jamais complètes (parlant d'expérience comme quelqu'un qui a essayé de créer mpld3 certains il y a des années).

La deuxième option n'est pas aussi agréable du point de vue de l'interchangeabilité, mais permettrait d'ajouter d'autres backends avec un ensemble d'attentes plus raisonnables.

Je pense que cela dépend du backend sur ce qu'ils font avec eux. Atteindre 100 %
la compatibilité entre les backends n'est pas vraiment faisable,
puisque le type de retour ne sera plus un matplotlib Axes. Et si
nous ne sommes pas compatibles sur le type de retour, je ne pense pas aux backends
devrait se plier en quatre pour essayer de gérer tous les arguments de mot-clé possibles.

Je pense donc que les pandas devraient documenter que **kwargs sera transmis à
le moteur de traçage sous-jacent, et ils peuvent faire ce qu'ils veulent avec
eux.

Le lundi 10 juin 2019 à 10h42 Jake Vanderplas [email protected]
a écrit:

Quelle est l'intention ici concernant les kwargs supplémentaires passés au traçage
les fonctions? Si des backends supplémentaires tentent de dupliquer le
fonctionnalité de toutes les personnalisations de tracé de style matplotlib, ou devraient-elles
autoriser la transmission de mots-clés correspondant à ceux utilisés par le particulier
arrière-plan ?

La première option serait bien en théorie, mais nécessiterait chaque
backend de traçage non matplotlib pour implémenter essentiellement son propre matplotlib
couche de conversion avec une longue queue d'incompatibilités qui
essentiellement jamais être complet (parlant d'expérience comme quelqu'un qui
essayé de créer mpld3 il y a quelques années).

La deuxième option n'est pas aussi agréable du point de vue de
l'interchangeabilité, mais permettrait d'ajouter d'autres backends avec une plus
ensemble raisonnable d'attentes.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOIS3IBV4XSSY7BPSCF3PZZY5LA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63DNMVXWWHJ2GO50
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AAKAOIQ3GYOGAPUZ4LSNK2DPZZY5LANCNFSM4HWIMEKQ
.

Je suis désolé si c'est une question stupide, mais si vous définissez une "API" de traçage qui est essentiellement un groupe de tracés prédéfinis, chaque backend ne produirait-il pas plus ou moins la même sortie? quelle nouvelle capacité cela est-il censé activer ? quelque chose comme un exportateur de pandas à vega peut-être ?

Je ne pense pas qu'il soit correct de dire que chaque backend produit plus ou moins la même sortie.

Par exemple, matplotlib est vraiment bon pour les graphiques statiques, mais pas très bon pour produire des graphiques interactifs portables.

D'autre part, bokeh, altair, et al. sont parfaits pour les graphiques interactifs, mais ne sont pas aussi matures que matplotlib pour les graphiques statiques.

Pouvoir produire les deux avec la même API serait une grande victoire.

La première option serait bien en théorie, mais nécessiterait que chaque backend de traçage non-matplotlib implémente essentiellement sa propre couche de conversion matplotlib avec une longue queue d'incompatibilités qui ne seraient pratiquement jamais complètes (parlant d'expérience comme quelqu'un qui a essayé de créer mpld3 certains il y a des années).

et épingle également Matplotlib encore plus que nous ne le sommes déjà en ce qui concerne les API. Je pense qu'il est logique que les pandas déclarent les boutons de style qu'ils souhaitent exposer et s'attendent à ce que les implémentations backend déterminent ce que cela signifie. Cela peut signifier _pas_ passer aveuglément **kwargs et s'assurer à la place que les objets renvoyés sont "la bonne chose" pour que le backend donné puisse effectuer une personnalisation de style après coup.

Par exemple, matplotlib est vraiment bon pour les graphiques statiques, mais pas très bon pour produire des graphiques interactifs portables.

Merci @jakevdp , oui, prendre en charge les graphiques interactifs est un bon objectif.

Avant que les choses n'aillent trop loin dans cette voie particulière, voici une solution alternative.

Au lieu de proclamer que l'API de traçage des pandas est désormais une spécification et de demander aux packages viz de l'implémenter spécifiquement, pourquoi ne pas générer une représentation intermédiaire (comme un fichier JSON vega) de l'intrigue, et encourager les backends à cibler cela comme entrée.

Les avantages incluent :

  1. Ne pas être lié au pouvoir expressif d'une API de pandas réifiée, qui n'a pas été conçue comme une spécification.
  2. Le travail effectué en traçant les packages pour prendre en charge les pandas devient disponible pour d'autres packages pydata qui génèrent des IR.
  3. Promouvoir un langage commun pour la visualisation des échanges dans l'espace pydata
  4. Ce qui rend le nouvel outil plus puissant car plus largement applicable
  5. Ce qui rend l'effort de les écrire plus raisonnable. Fondamentalement, des incitations améliorées.

Vega/Vega-lite , en tant que langage de spécification de visualisation moderne, établi, ouvert et basé sur JSON, plusieurs années-homme l'ont mis dans sa conception et sa mise en œuvre, et les outils existants construits autour de lui, semblent avoir été créés expressément à cette fin . ( s'il vous plaît ne le faites pas ).

Vous savez, frontend->IR->backend , comme les compilateurs sont conçus.

Au moins trois packages implémentent déjà l'API. Tout ce que les pandas doivent faire est d'offrir une option pour changer le backend et documenter son utilisation, ce qui semble être un bon rapport qualité-prix.

Le 15 juin 2019, à 16h28, pilkibun [email protected] a écrit :

Par exemple, matplotlib est vraiment bon pour les graphiques statiques, mais pas très bon pour produire des graphiques interactifs portables.

Merci @jakevdp , oui, prendre en charge les graphiques interactifs est un bon objectif.

Avant que les choses n'aillent trop loin dans cette voie particulière, voici une solution alternative.

Au lieu de proclamer que l'API de traçage des pandas est désormais une spécification et de demander aux packages viz de l'implémenter spécifiquement, pourquoi ne pas générer une représentation intermédiaire (comme un fichier JSON vega) de l'intrigue, et encourager les backends à cibler cela comme entrée.

Les avantages incluent :

Ne pas être lié au pouvoir expressif d'une API de pandas réifiée, qui n'a pas été conçue comme une spécification.
Le travail effectué en traçant les packages pour prendre en charge les pandas devient disponible pour d'autres packages pydata qui génèrent des IR.
Promouvoir un langage commun pour la visualisation des échanges dans l'espace pydata
Ce qui rend le nouvel outil plus puissant car plus largement applicable
Ce qui rend l'effort de les écrire plus raisonnable. Fondamentalement, des incitations améliorées.
Vega/Vega-lite, en tant que langage de spécification de visualisation moderne, établi, ouvert et basé sur JSON, plusieurs années-homme l'ont mis dans sa conception et sa mise en œuvre, et les outils existants construits autour de lui, semblent avoir été créés expressément à cette fin . (s'il vous plaît ne le faites pas).

Vous savez, frontend->IR->backend, comme les compilateurs sont conçus.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, affichez-le sur GitHub ou coupez le fil de discussion.

Nous avons maintenant fusionné #26753, et le backend de traçage peut être modifié à partir des pandas. Lorsque nous avons divisé le code matplotlib, nous avons laissé les SeriesPlotMethods et FramePlotMethods du côté des pandas (pas matplotlib). C'était principalement pour laisser les docstrings du côté des pandas.

Mais je vois que ce que les backends ont fait était de réimplémenter ces classes. Donc, actuellement, nous nous attendons à ce que les backends aient une classe par parcelle (par exemple LinePlot , BarPlot ), mais à la place ils implémentent une classe avec une parcelle par méthode (par exemple hvPlot, or the same names as pandas for pdvega `).

Ce que je pense logique, du moins dans une première version, c'est que nous implémentions l'API comme hvplot et pdvega fait. Je voudrais juste créer une classe abstraite dans les pandas, dont les backends héritent.

Si cela a du sens pour tout le monde, je vais commencer par créer la classe abstraite et adapter le backend matplotlib que nous avons dans les pandas, et une fois cela fait, nous adaptons hvplot et pdvega (les changements il devrait être assez petit).

Les pensées?

Ce que je pense logique, au moins dans une première version, c'est que nous implémentions l'API comme hvplot et pdvega l'ont fait. Je voudrais juste créer une classe abstraite dans les pandas, dont les backends héritent.

Je pense que dans l'ensemble, cette approche sera plus propre. Je ne peux pas parler à d'autres moteurs de traçage, mais au moins dans hvPlot, différentes méthodes de tracé partagent un peu de code, par exemple scatter , line et area sont en grande partie analogues, et Je préférerais ne pas compter sur la sous-classe pour partager du code entre eux. De plus, je pense que différents backends devraient avoir la possibilité d'ajouter des types de tracés supplémentaires et les exposer en tant que méthodes publiques supplémentaires semble être l'approche la plus simple et la plus naturelle.

Juste pour être sûr de bien comprendre, quand vous dites I'd prefer not to rely on subclassing to share code between them vous voulez dire comme dans class LinePlot(MPLPlot) , n'est-ce pas ? Et non pas que vous pensiez que c'est une mauvaise idée d'hériter d'une classe de base abstraite ?

Je pense que je suis +1 pour laisser les backends définir des types de tracés pas dans les pandas. Mais je ne vais probablement pas le mettre en œuvre maintenant. Nous prévoyons de relâcher les pandas dans environ une semaine. Et je pense que cela nécessitera un peu plus de réflexion que d'appeler aveuglément les méthodes des backends si l'utilisateur fournit kind='foo' et que le backend fournit la méthode foo (par exemple, la validation des paramètres, ou cela provoquera que certains kind seront dans la documentation et d'autres non).

Juste pour être sûr de bien comprendre, quand vous dites que je préférerais ne pas compter sur les sous-classes pour partager du code entre eux, vous voulez dire comme dans la classe LinePlot(MPLPlot), n'est-ce pas ? Et non pas que vous pensiez que c'est une mauvaise idée d'hériter d'une classe de base abstraite ?

Oui c'est vrai. Plus concrètement je préférerais ne pas avoir à faire ce genre de chose :

class MPL1dPlot(MPLPlot):

    def _some_shared_method(self, ...):
        ...

class LinePlot(MPL1dPlot):
    ...

class AreaPlot(MPL1dPlot):
    ...

Désolé si ce n'était pas clair.

Tout à fait en faveur d'une API plus simple qui est exposée publiquement en tant que fonction unique au lieu des classes comme maintenant proposé dans https://github.com/pandas-dev/pandas/pull/27009.

Question/remarque générale sur le fonctionnement actuel de l'option backend. Supposons que je sois le développeur pdvega et que ce backend soit disponible. Cela signifie que si les utilisateurs font pd.options.plotting.backend = 'pdvega' , que la bibliothèque pdvega doit avoir une fonction plot supérieur ?
1) en tant qu'auteur de bibliothèque, ce n'est pas nécessairement la fonction que vous souhaitez exposer publiquement (c'est-à-dire que pour la méthode plot niveau supérieur du point de vue de la bibliothèque, ce n'est pas nécessairement l'API que vous voulez que vos utilisateurs à utiliser directement) et 2) dans ce cas, vous voudrez peut-être pouvoir faire pd.options.plotting.backend = 'altair' ? (au cas où les développeurs altair seraient d'accord avec ça)
Donc, en gros, ma question est la suivante : doit-il y avoir un mappage exact 1:1 sur le nom du backend et qu'est-ce qui est importé ? (ce qui est maintenant nécessaire car il effectue simplement une importation de cette chaîne de backend fournie).

EDIT : je vois qu'en fait quelque chose de similaire a été discuté dans le PR #26753

Si nous décidons que les pandas ne savent pas/limitent quels backends peuvent être utilisés (ce que je suis fortement en faveur de faire), nous devons décider comment/quoi appeler les backends.

Ce qui a été implémenté et proposé dans le PR sur lequel je travaille, c'est que l'option plotting.backend est un module (peut être pdvega , altair , altair.pandas , ou autre), et ce module doit avoir une fonction publique plot , c'est ce que nous appellerons.

Nous pouvons envisager d'autres options, comme si l'option est pdvega , nous importons pdvega.pandas , ou nous pouvons nommer la fonction plot_pandas ou autre. Je pense que la manière proposée est la plus simple, mais s'il y a d'autres propositions qui ont plus de sens, je suis heureux de la changer.

Une autre discussion est de savoir si nous voulons forcer les utilisateurs à importer les backends manuellement :

import pandas
import hvplot

pandas.Series([1, 2, 3]).plot()

Si nous faisons cela, les modules peuvent s'enregistrer eux-mêmes, ils peuvent également enregistrer des alias (donc set_option peut comprendre d'autres noms que le nom du module). Ils peuvent également implémenter des fonctions ou des machines personnalisées (par exemple des gestionnaires de contexte) pour tracer avec certains backends, ... Personnellement, je pense que plus nous gardons les choses simples, mieux c'est.

Et bien qu'il puisse être agréable de faire pandas.set_option('plotting.backend', 'bokeh') pour tracer en bokeh, je pense que cela implique deux choses que je n'aime personnellement pas :

  • pandas.set_option('plotting.backend', 'bokeh') ne fonctionnera que si import pandas_bokeh a été appelé, et sera déroutant pour les utilisateurs.
  • Cela implique également qu'il n'y a qu'un seul module à tracer dans bokeh . Ce qui n'a pas besoin d'être vrai et donne la fausse impression aux utilisateurs que vous tracez directement avec le bokeh, et non avec un backend de traçage de pandas pour le bokeh.

@datapythonista merci pour la réponse détaillée. Je suis d'accord pour le garder maintenant tel quel pour la version initiale (la possibilité d'un alias peut toujours être ajoutée plus tard).

Si les utilisateurs veulent le tracé de la courbe d'Andrew de hvplot, ils doivent importer la fonction de hvplot et y passer la trame de données.

+1, je n'exposerais pas non plus toutes les fonctions de traçage supplémentaires via le backend.

Mais à propos de leur déplacement vers pandas.plotting.matplotlib , cela me semble être une rupture incompatible avec l'arrière inutile (en supposant que vous vouliez ne pas simplement déplacer l'implémentation).

pandas.set_option('plotting.backend', 'bokeh') ne fonctionnera que si import pandas_bokeh a été appelé, et sera déroutant pour les utilisateurs.

Si nous utilisons des points d'

De plus, pour ce que ça vaut, une fois que cela est entré, je pense que je déprécierais probablement pdvega et déplacerais le code pertinent vers un nouveau package nommé pandas_altair ou quelque chose de similaire.

@datapythonista Je pense que nous devrions décider de la portée de l'API backend de traçage avant 0.25.0 (pas pour le RC cependant).

Vous êtes favorable au maintien des fonctions de traçage diverses (ainsi que hist / boxplot) ?

@datapythonista fermer cela alors que nous avons fusionné le PR ?

@jreback Je garderais cela ouvert jusqu'à ce que nous nous mettions d'accord sur l'API, @TomAugspurger et @jorisvandenbossche ne voulaient rien déléguer au backend, à l'exception des tracés d'accesseurs.

Ce que je ferais pour les pandas de traçage - le backend est le suivant.

Pour la sortie :

  • Laissez les choses telles qu'elles sont, hvplot implémente tout, toutes les parcelles, celles des accesseurs et celles qui ne le sont pas. Et je pense que tout déléguer rend les choses simples.
  • Je ne sais pas si j'exclurais de ce qui précède les register_converters. Au moins, nous devrions changer le nom de register_matplotlib_converters nous les déléguons

Pour la prochaine version :

  • Je déprécierais tous les doublons pandas.plotting.boxplot , Series.hist ,...
  • Je déplacerais tous les tracés à appeler à partir des accesseurs (andrew_curves, radviz, parallel_curves,...).

Pour une version initiale de l'API backend, je préférerais être plus conservateur dans ce que nous exposons, plutôt que d'inclure tout. Il est beaucoup plus facile d'ajouter des choses plus tard que d'enlever.

Personnellement, je ne déplacerais pas non plus tous ces tracés divers vers l'accesseur (il pourrait y avoir quelques exceptions, comme la matrice de dispersion), IMO, andrew_curves et radviz, etc. ne "valent" pas une méthode.

Cela dit : voulons-nous autoriser les backends à implémenter des « types » supplémentaires ? Nous n'avons donc pas à décider, en tant que pandas, quelles méthodes d'accès peuvent être disponibles. Si l'utilisateur passe un certain kind ou essaie d'accéder à un attribut, nous pouvons toujours le transmettre au backend plot avec un __getattribute__ .

Juste pour expliquer un peu pourquoi les choses sont comme elles sont maintenant. C'est pertinent car je ne sais pas trop comment mettre en œuvre les changements que vous proposez, ou ne pas exposer les choses en général. Je ne dis pas ici que ça ne peut pas se faire autrement, c'est juste pour enrichir la discussion.

La première décision a été de déplacer tout le code à l'aide de matplotlib vers un module séparé ( pandas.plotting._matplotlib ). En faisant cela, ce module est en quelque sorte devenu le backend matplotlib.

Tout ce qui était public dans pandas.plotting est resté public. Et pour rendre les choses aussi simples que possible, chacune de ces fonctions, une fois appelée, charge le backend (appel à _get_plot_backend ) et appelle la fonction là-bas.

L'API publique pour l'utilisateur n'a pas du tout changé, les utilisateurs ont toujours les mêmes méthodes et fonctions disponibles. Nous n'exposons rien de nouveau.

Comment je comprends les choses, si nous décidons qu'un tracé existant comme andrew_curves n'est pas délégué au backend, cela implique qu'au lieu d'obtenir le backend sélectionné par l'utilisateur, nous sélectionnerons toujours le backend matplotlib. Étant donné qu'au moins hvplot implémente déjà andrew_curves , personnellement, je ne vois pas l'intérêt. Si l'utilisateur veut un andrew_curves plot dans matplotlib, c'est aussi simple que de ne pas changer le backend (ou de le redéfinir s'il a été modifié). Ainsi, avec le changement, nous rendrions simplement la vie des utilisateurs beaucoup plus difficile, en ajoutant une complexité supplémentaire aux pandas.

Si nous voulons être gentils avec les développeurs backend et ne pas les forcer à implémenter des intrigues qui ne sont peut-être pas si courantes (je suppose que c'est l'un des raisonnements ?), Peut-être pouvons-nous utiliser par défaut le backend matplotlib tout ce qui manque dans le backend sélectionné ?

À propos de la délégation de tout type de complot inconnu au backend, je suis -1 pour le faire en ce moment. Cela peut sûrement avoir du sens à terme. Mais je pense que le fait d'avoir plusieurs types d'intrigues documentés dans les pandas, et d'en avoir d'autres que nous ne documentons pas, semble un peu difficile. Je pense que cela peut attendre la prochaine version, après que nous ayons eu des retours sur la façon dont les différents backends fonctionnent pour les utilisateurs, et nous aurons plus de temps pour discuter et analyser en détail.

Si l'utilisateur veut un tracé andrew_curves dans matplotlib, c'est aussi simple que de ne pas changer le backend (ou de le redéfinir s'il a été modifié). Ainsi, avec le changement, nous rendrions simplement la vie des utilisateurs beaucoup plus difficile, en ajoutant une complexité supplémentaire aux pandas.

Je ne pense pas que nous rendrions la vie de l'utilisateur plus difficile. Au lieu de l'importer depuis pandas.plotting, s'ils veulent une version de hvplot, ils peuvent simplement l'importer à partir de là. Ce qui n'est pas possible pour la méthode DataFrame.plot, telle qu'elle est définie sur l'objet. Pour moi, c'est la raison principale du backend de traçage.

Si nous voulons être gentils avec les développeurs backend et ne pas les forcer à implémenter des intrigues qui ne sont peut-être pas si courantes

Pour moi, il ne s'agit pas d'être gentil ou que tout implémenter serait nécessaire (c'est tout à fait bien si un backend ne prend pas en charge tous les types de traçage, IMO), mais plutôt une extension inutile de l'API backend de traçage, qui s'y lie également .
Si nous redémarrions les pandas à partir de zéro, je ne pense pas que ces types de traçage divers seraient inclus. Mais avec l'API backend de traçage, nous commençons en quelque sorte quelque chose de nouveau.

D'autres avis à ce sujet ?

D'accord avec @jorisvandenbossche.


Juste pour vous assurer que cela n'est pas perdu, je pense que la suggestion de @jakevdp d'utiliser les points d'entrée de setuptool mérite d'être envisagée pour résoudre le problème d'enregistrement des commandes d'importation : https://github.com/pandas-dev/pandas/issues/26747 #issuecommentaire -507415929

@jorisvandenbossche, comment

Puisque vous mentionnez que dans un panda à partir de zéro, nous n'inclurions pas ces intrigues, devrions-nous les déprécier ? Je suis +1 pour déplacer tous les tracés qui ne sont pas des méthodes de Series ou DataFrame vers un package tiers. Ou si l'un est suffisamment important pour être conservé, le déplacer pour être appelé avec .plot() comme les autres.

je déprécierais les parcelles non standard dans les pandas
et passer à un package externe

Joris est hors ligne pendant un moment.

Je pense que lorsque nous avons discuté de cela dans le passé, sa position et la mienne sur les thèses est de les laisser intactes jusqu'à ce qu'elles deviennent un fardeau de maintenance.

Juste pour que nous soyons sur la même page, voici un résumé de ce que nous avons et ma compréhension de l'état de la discussion :

Utilisé comme méthodes de Series et DataFrame (si je ne m'abuse, nous sommes tous heureux de les garder tels quels, délégués au backend sélectionné) :

  • PlotAccessor
  • boxplot_frame
  • boxplot_frame_groupby
  • hist_frame
  • hist_series

Autres tracés (en discussion pour savoir s'ils doivent être dépréciés, délégués au backend matplotlib ou délégués au backend sélectionné) :

  • boîte à moustaches
  • scatter_matrix
  • radviz
  • andrews_curves
  • bootstrap_plot
  • coordonnées_parallèles
  • lag_plot
  • autocorrelation_plot
  • table

Autres contenus publics dans pandas.plotting (en cours de discussion également) :

  • plot_params
  • register_matplotlib_converters
  • deregister_matplotlib_converters

Pour la section Other plots , je pense personnellement qu'ils sont un fardeau de maintenance à ce stade, et je suis +1 pour les sortir des pandas et les déprécier en 0.25.

Pour les convertisseurs et les autres trucs, ce que nous avons maintenant n'est sûrement pas correct, puisque register_matplotlib_converters délègue au tracé sélectionné, qui ne peut pas être matplotlib. Les options que je suppose que nous pouvons envisager sont:

  • Renommez-les en register_converters / deregister_converters , désapprouvez les actuels et continuez à déléguer au backend
  • Déplacez-les de pandas.plotting à pandas.plotting.matplotlib (ce qui impliquerait de rendre le backend matplotlib public, donc je ne le ferais pas)
  • Laissez-les tels quels et déléguez au backend matplotlib au lieu du backend sélectionné (je vois cela plus comme un hack qu'une bonne décision de conception, je préférerais garder pandas.plotting agnostique des backends existants)

Pour la section Autres parcelles, je pense personnellement qu'elles constituent un fardeau de maintenance à ce stade, et je suis +1 pour les sortir des pandas et les déprécier en 0.25.

Comment trouvez-vous que les « autres parcelles » représentent une charge d'entretien ? En regardant l'historique des parcelles "misc": https://github.com/pandas-dev/pandas/commits/0.24.x/pandas/plotting/_misc.py , nous avons ~ 10-15 commits depuis 2017. Le la majorité sont des nettoyages globaux appliqués à l'ensemble de la base de code (donc une petite charge marginale). Je ne vois que 1-2 commits modifiant la documentation, et aucun commit ne changeant de fonctionnalité.

Renommez-les en register_converters/deregister_converters, désapprouvez les actuels et continuez à déléguer au backend

Je ne pense pas que cela aurait du sens. Il existe des convertisseurs spécifiques à matplotlib que nous avons écrits pour matplotlib. Les autres backends ne les auront pas. Il ne devrait probablement pas faire partie de l'API backend.

Je ne voulais pas dire que ces parcelles sont un fardeau en raison de la quantité de maintenance que nous avons eue au cours des derniers mois, mais à cause du problème qu'elles supposent maintenant d'avoir une API cohérente et intuitive pour les utilisateurs, et un bon module conception de code pour nous.

En ce qui concerne les convertisseurs, je ne sais pas si les auteurs backend voudront peut-être implémenter l'équivalent de ceux de matplotlib dans certains cas. Mais cela ne semble pas un problème s'ils ne le font pas, et ces fonctions ne font rien pour certains ou tous les autres backends. Je suis également d'accord avec l'option 2, mais je ne la trouve pas aussi soignée.

mais à cause du problème qu'ils supposent maintenant d'avoir une API cohérente et intuitive pour les utilisateurs, et une bonne conception de code modulaire pour nous.

Cependant, ils sont déjà quelque peu incohérents avec DataFrame.plot. Le nom "misc" implique que :) Est-ce que le fait d'avoir un backend échangeable aggrave la situation ? Dans la mesure où cela vaut la peine de s'attarder sur le code utilisateur ? Je ne pense pas.

Je ne sais pas si les auteurs backend voudront peut-être implémenter l'équivalent de ceux de matplotlib dans certains cas.

Je ne pense pas. Le but de ces convertisseurs est d'enseigner à matplotlib les objets pandas. Les bibliothèques implémentant le backend n'auront pas ce problème, car elles dépendent déjà des pandas.

Personnellement, j'y pense principalement en termes de gestion de la complexité. Avoir une API de traçage standard qui est déléguée au backend via une API unique est facile à comprendre et à maintenir. Les utilisateurs et les responsables ont juste besoin d'apprendre qu'il existe une fonction plot avec un argument kind , et que cela sera exécuté dans le backend sélectionné.

Avoir dans le backend un ensemble de tracés hétérogènes, qui en plus de ne pas suivre la même API, utilisent un backend, mais pas celui sélectionné pour les autres tracés, mais celui de Matplotlib, ajoute trop de complexité pour tout le monde à mon humble avis.

Et le coût de leur déplacement me semble faible, je suppose que peu de nos utilisateurs connaissent même ces parcelles. Et pour ceux qui le font, ils auront juste besoin d'installer un package conda supplémentaire et d'utiliser import pandas_plotting; pandas_plotting.andrews_curves(df) au lieu de pandas.plotting.andrews_curves(df) .

Cela me semble beaucoup à gagner, à moindre coût, mais bien sûr ce n'est qu'un avis.

Pouvons-nous documenter que le backend échangeable est uniquement pour Series/DataFrame.plot ? Cela semble être une règle assez simple.

J'ai l'impression d'être un hack qui m'ajoute une complexité inutile ; Je ne pense pas que l'expliquer dans la documentation le rende moins contre-intuitif.

Mais de toute façon, pas grave. Si c'est l'option préférée, voici comment je l'implémenterais, au moins l'augmentation de la complexité du code est minime : #27432

En regardant cela de plus près maintenant : si je comprends bien, la façon dont le backend de traçage sera défini utilise :

pd.set_option('plotting.backend', 'name_of_module')

Ma compréhension, alors, est que si je veux faire le travail suivant:

pd.set_option('plotting.backend', 'altair')

alors j'aurai besoin du package altair de niveau supérieur pour définir toutes les fonctions dans https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py. Je préférerais ne pas polluer l'espace de noms de premier niveau d'Altair avec toutes ces API supplémentaires qui ne sont pas destinées à être utilisées par les utilisateurs d'Altair. En fait, je préférerais que l'extension pandas d'Altair vive dans un package séparé, donc ce n'est pas lié à la cadence de sortie d'Altair lui-même.

Si je comprends bien, cela signifie qu'il n'y a aucun moyen pour moi de faire fonctionner correctement pd.set_option('plotting.backend', 'altair') sans coder en dur le package altair dans les pandas de la manière dont matplotlib est actuellement codé en dur, est-ce correct ?

https://github.com/pandas-dev/pandas/blob/f1b9fc1fab93caa59aebcc738eed7813d9bd92ee/pandas/plotting/_core.py#L1550 -L1551

Si tel est le cas, je vous conseillerais fortement de repenser les moyens par lesquels cette API est exposée dans des packages tiers.

Ma solution suggérée serait d'adopter un framework basé sur le point d'entrée qui me permettrait, par exemple, de créer un package comme altair_pandas qui enregistre le point d'entrée altair pour implémenter l'API. Sinon, les utilisateurs seront à jamais confus que pd.set_option('plotting.backend', 'altair') ne fait pas ce qu'ils attendent.

D'accord. Je pense que les points d'entrée sont la voie à suivre. Je vais prototyper quelque chose.

Le vendredi 19 juillet 2019 à 13:16 Jake Vanderplas [email protected]
a écrit:

En y regardant de plus près maintenant : si je comprends bien, la façon dont
le moteur de traçage sera défini en utilisant :

pd.set_option('plotting.backend', 'name_of_module')

Ma compréhension, alors, est que si je veux faire le travail suivant:

pd.set_option('tracé.backend', 'altair')

alors j'aurai besoin du package altair de niveau supérieur pour définir toutes les fonctions
dans
https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py.
Je préférerais ne pas polluer l'espace de noms de premier niveau d'Altair avec tous ces
API supplémentaires. En fait, je préférerais que l'extension des pandas d'Altair soit
vivre dans un package séparé, il n'est donc pas lié à la cadence de sortie de
Altaïr lui-même.

Si je comprends bien, cela signifie qu'il n'y a aucun moyen pour moi de faire pd.set_option('plotting.backend',
'altair') fonctionnent correctement sans coder en dur le package altair dans les pandas
la façon dont matplotlib est actuellement codé en dur, est-ce correct ?

Si tel est le cas, je vous conseillerais fortement de repenser la façon dont cela est activé par
packages tiers. En particulier, l'adoption d'un cadre basé sur les points d'entrée
me laisserait créer un package comme altair_pandas qui enregistre l'altair
point d'accès. Sinon, les utilisateurs seront à jamais confus que pd.set_option('plotting.backend',
'altair') ne fait pas ce qu'ils attendent.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOITQM7HH5X4SZ4IAPS3QAIBA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LN5MVXH13O
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AAKAOISFLHDGXLGQ3PUMNLDQAIAIBANCNFSM4HWIMEKQ
.

Il fut un temps où ce que vous disiez était en grande partie correct, mais ce n'est plus le cas.

Si vous voulez pandas.options.plotting.backend = 'altair' , en 0.25 vous avez juste besoin d'avoir une fonction altair.plot() . À un moment donné, j'ai pensé qu'il serait préférable d'appeler la fonction pandas_plot au lieu de simplement plot , donc c'était spécifique dans un backend qui avait d'autres choses, mais nous n'avons finalement pas fait le changement.

Si la création de la fonction plot dans le niveau supérieur d'altair pose un problème, nous pouvons la renommer dans une future version, ou vous pouvez également avoir altair.pandas.plot , mais les utilisateurs devront alors définir pandas.options.plotting.backend = 'altair.pandas' .

Vous pouvez sûrement changer l'option vous-même une fois que les utilisateurs ont effectué un import altair . Et nous pourrions implémenter un registre de backends. Mais je pense que ce serait déroutant pour les utilisateurs s'ils font le pandas.options.plotting.backend = 'altair' et que cela échoue, car ils ont oublié le import altair auparavant.

Une dernière chose est de considérer que nous pourrions éventuellement avoir plus d'un backend pandas implémenté pour altair (ou toute autre bibliothèque de visualisation). Donc, pour moi, que le nom du backend ne soit pas altair , n'est pas nécessairement une mauvaise chose.

Voici une implémentation basée sur les points d'entrée

diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
index 0610780ed..c8ac12901 100644
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -1532,8 +1532,10 @@ class PlotAccessor(PandasObject):

         return self(kind="hexbin", x=x, y=y, C=C, **kwargs)

+_backends = {}

-def _get_plot_backend(backend=None):
+
+def _get_plot_backend(backend="matplotlib"):
     """
     Return the plotting backend to use (e.g. `pandas.plotting._matplotlib`).

@@ -1546,7 +1548,14 @@ def _get_plot_backend(backend=None):
     The backend is imported lazily, as matplotlib is a soft dependency, and
     pandas can be used without it being installed.
     """
-    backend_str = backend or pandas.get_option("plotting.backend")
-    if backend_str == "matplotlib":
-        backend_str = "pandas.plotting._matplotlib"
-    return importlib.import_module(backend_str)
+    import pkg_resources  # slow import. Delay
+    if backend in _backends:
+        return _backends[backend]
+
+    for entry_point in pkg_resources.iter_entry_points("pandas_plotting_backends"):
+        _backends[entry_point.name] = entry_point.load()
+
+    try:
+        return _backends[backend]
+    except KeyError:
+        raise ValueError("No backend {}".format(backend))
diff --git a/setup.py b/setup.py
index 53e12da53..d2c6b18b8 100755
--- a/setup.py
+++ b/setup.py
@@ -830,5 +830,10 @@ setup(
             "hypothesis>=3.58",
         ]
     },
+    entry_points={
+        "pandas_plotting_backends": [
+            "matplotlib = pandas:plotting._matplotlib",
+        ],
+    },
     **setuptools_kwargs
 )

Je pense que c'est plutôt sympa. Les packages tiers modifieront leur setup.py (ou pyproject.toml) pour inclure quelque chose comme

entry_points={
    "pandas_plotting_backends": ["altair = pdvega._pandas_plotting_backend"]
}

J'aime que cela brise le couplage étroit entre le nommage et la mise en œuvre.

Je n'ai pas travaillé avec des points d'entrée, sont-ils comme un registre global de l'environnement Python ? Étant nouveau pour eux, je n'aime pas l'idée, mais je suppose que ce serait une façon raisonnable de le faire alors.

J'aimerais toujours avoir les deux options, donc si l'utilisateur fait pandas.options.plottting.backend = 'my_own_project.my_custom_small_backend' cela fonctionne et ne nécessite pas de créer un package ni de définir des points d'entrée.

Je n'ai pas travaillé avec des points d'entrée, sont-ils comme un registre global de l'environnement Python ?

Je ne les ai pas utilisés non plus, mais je pense que c'est l'idée. D'après ce que j'ai compris, ils proviennent de setuptools (mais des packages comme flit s'y accrochent ?). Ils ne font donc pas partie de la bibliothèque standard, mais setuptools est ce que tout le monde utilise de toute façon.

J'aimerais toujours avoir les deux options

Revenir à import_module(backend_name) semble raisonnable.

Cette page vous a été utile?
0 / 5 - 0 notes