Pandas: API: definir API para pandas que trazan backends

Creado en 9 jun. 2019  ·  44Comentarios  ·  Fuente: pandas-dev/pandas

En # 26414 dividimos el módulo de trazado de pandas en un marco de trazado general capaz de llamar a diferentes backends y los actuales backends de matplotlib. La idea es que otros backends se puedan implementar de una manera más simple y los usuarios de pandas puedan usarlos con una API común.

La API definida por el backend de matplotlib actual incluye los objetos que se enumeran a continuación, pero esta API probablemente se pueda simplificar. Aquí está la lista con preguntas / propuestas:

Métodos no controvertidos para mantener en la API (proporcionan la funcionalidad Series.plot(kind='line') ...):

  • LinePlot
  • BarPlot
  • BarhPlot
  • HistPlot
  • BoxPlot
  • KdePlot
  • AreaPlot
  • PiePlot
  • Gráfico de dispersión
  • HexBinPlot

Funciones de trazado proporcionadas en pandas (por ejemplo, pandas.plotting.andrews_curves(df) )

  • andrews_curves
  • autocorrelation_plot
  • bootstrap_plot
  • lag_plot
  • coordenadas_paralelas
  • radviz
  • scatter_matrix
  • mesa

¿Deberían ser parte de la API y otros backends también deberían implementarlos? ¿Tendría sentido convertir al formato .plot (por ejemplo, DataFrame.plot(kind='autocorrelation') ...)? ¿Tiene sentido mantenerse fuera de la API o pasar a un módulo de terceros?

Métodos redundantes que posiblemente se pueden eliminar:

  • hist_series
  • hist_frame
  • diagrama de caja
  • boxplot_frame
  • boxplot_frame_groupby

En el caso de boxplot , actualmente tenemos varias formas de generar una gráfica (llamando principalmente al mismo código):

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

Personalmente, desaprobaría el número 4 y, para el número 3, desaprobaría o al menos no requeriría un método boxplot_frame separado en el backend, pero trataría de reutilizar BoxPlot (para los comentarios del número 3, lo mismo se aplica a hist ).

Por boxplot_frame_groupby , no verifiqué en detalle, pero ¿no está seguro de si BoxPlot podría reutilizarse para esto?

Funciones para registrar convertidores:

  • Registrarse
  • dar de baja

¿Tienen sentido para otros backends?

En desuso en pandas 0.23, se eliminará:

  • tsplot

Para ver qué hace cada una de estas funciones en la práctica, puede ser útil este cuaderno 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

Comentario más útil

Aquí hay una implementación basada en puntos de entrada.

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
 )

Creo que es bastante agradable. Los paquetes de terceros modificarán su setup.py (o pyproject.toml) para incluir algo como

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

Me gusta que rompe el estrecho vínculo entre naming e implementación.

Todos 44 comentarios

Creo que mantener cosas como la autocorrelación fuera de la API de backend intercambiable.

Creo que hemos dejado cosas como df.boxplot e hist porque tienen un comportamiento ligeramente diferente al de la API .plot. No recomendaría hacerlos parte de la API de backend.

Aquí está mi comienzo en una API de backend propuesta desde hace unos meses: https://github.com/TomAugspurger/pandas/commit/b07aba28a37b0291fd96a1f571848a7be2b6de8d

Creo que vale la pena mencionar que al menos hvplot (no marcó el resto) ya proporciona funciones como andrews_curves , scatter_matrix , lag_plot ,. ..

¿Puede ser que si no queremos forzar a todos los backends a implementarlos, podemos verificar si el backend seleccionado los implementa, y por defecto a los diagramas de matplotlib?

Supuse que boxplot y hist comportaron exactamente igual, pero solo tenía atajos Series.hist() por Series.plot.hist() . El "atajo" muestra la cuadrícula de la trama, pero aparte de eso, no he visto ninguna diferencia.

En mi opinión, el valor principal de esta opción es el espacio .plot nombres

Si los usuarios quieren la gráfica de la curva de Andrew de hvplot, deben importar la función
desde hvplot y pase el marco de datos allí.

El domingo, 9 de junio de 2019 a las 7:17 a.m., Marc García [email protected] escribió:

Creo que vale la pena mencionar que al menos hvplot (no marcó el
rest) ya proporciona funciones como andrews_curves,
scatter_matrix, lag_plot, ...

Puede ser que si no queremos forzar a todos los backends a implementarlos, podemos
comprobar si el backend seleccionado los implementa, y por defecto a la
parcelas matplotlib?

Supuse que boxplot e hist se comportaban exactamente igual, pero acababa de
atajos Series.hist () para Series.plot.hist (). El "atajo" muestra el
trazar cuadrícula, pero aparte de eso, no he visto ninguna diferencia.

-
Recibes esto porque estás en un equipo que se mencionó.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOIRLJHBMXMXKK2IG2NDPZTYFPA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXWH3WWK3TUL52HS4DFVREXWG43WZVMVMVDFVREXWG43WNMV
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAKAOISDHL6H7PVOOJAQXELPZTYFPANCNFSM4HWIMEKQ
.

Creo que tiene sentido, pero si hacemos eso, creo que deberíamos moverlos a pandas.plotting.matplotlib.andrews_curves , en lugar de pandas.plotting.andrews_curves .

@TomAugspurger Necesito verificar con más detalle, pero creo que la API que implementó en https://github.com/TomAugspurger/pandas/commit/b07aba28a37b0291fd96a1f571848a7be2b6de8d es la que tiene más sentido. Trabajaré en ello una vez que termine # 26753. También experimentaré si es factible mover andrews_curves , scatter_matrix ... a la sintaxis .plot() , creo que eso hará las cosas más simples y fáciles para todos (nosotros , bibliotecas de terceros y usuarios).

¿Cuál es la intención aquí con respecto a los kwargs adicionales que se pasan a las funciones de trazado? ¿Deberían los backends adicionales intentar duplicar la funcionalidad de todas las personalizaciones de gráficos de estilo matplotlib, o deberían permitir que se pasen palabras clave que correspondan a las utilizadas por el backend en particular?

La primera opción sería buena en teoría, pero requeriría que cada backend de trazado que no sea de matplotlib implemente esencialmente su propia capa de conversión de matplotlib con una larga cola de incompatibilidades que esencialmente nunca estarán completas (hablando por experiencia como alguien que intentó crear mpld3 algunos años atrás).

La segunda opción no es tan agradable desde la perspectiva de la intercambiabilidad, pero permitiría agregar otros backends con un conjunto de expectativas más razonables.

Creo que eso depende del backend de lo que hagan con ellos. Alcanzando el 100%
la compatibilidad entre backends no es realmente factible,
ya que el tipo de retorno ya no será un matplotlib Axes. Y si
no somos compatibles en el tipo de retorno, no creo que los backends
debería hacer todo lo posible para tratar de manejar todos los posibles argumentos de palabras clave.

Así que creo que los pandas deberían documentar que **kwargs se transferirán a
el motor de trazado subyacente, y pueden hacer lo que quieran con
ellos.

El lunes, 10 de junio de 2019 a las 10:42 a. M. Jake Vanderplas [email protected]
escribió:

¿Cuál es la intención aquí con respecto a los kwargs adicionales pasados ​​a la conspiración?
funciones? En caso de que backends adicionales intenten duplicar el
funcionalidad de todas las personalizaciones de gráficos de estilo matplotlib, o deberían
Permitir que se pasen palabras clave que correspondan a las utilizadas por el
backend?

La primera opción sería buena en teoría, pero requeriría cada
backend de trazado que no es matplotlib para implementar esencialmente su propio matplotlib
capa de conversión con una larga cola de incompatibilidades que
esencialmente nunca ser completo (hablando por experiencia como alguien que
intentó crear mpld3 hace algunos años).

La segunda opción no es tan agradable desde la perspectiva de
intercambiabilidad, pero permitiría agregar otros backends con una mayor
un conjunto razonable de expectativas.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOIS3IBV4XSSY7BPSCF3PZZY5LA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVDVODXHG43VMTUL52HS4DFVDVDXHG43VM2
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAKAOIQ3GYOGAPUZ4LSNK2DPZZY5LANCNFSM4HWIMEKQ
.

Lo siento si esta es una pregunta estúpida, pero si define una "API" de trazado que es básicamente un grupo de gráficos enlatados, ¿no produciría cada backend más o menos el mismo resultado? ¿Qué nueva capacidad está destinada a habilitar? ¿Algo así como un pandas a exportador de vega quizás?

No creo que sea correcto decir que cada backend produce más o menos el mismo resultado.

Por ejemplo, matplotlib es realmente bueno en gráficos estáticos, pero no muy bien en la producción de gráficos interactivos portátiles.

Por otro lado, bokeh, altair, et al. son excelentes para gráficos interactivos, pero no son tan maduros como matplotlib para gráficos estáticos.

Ser capaz de producir ambos con la misma API sería una gran ventaja.

La primera opción sería buena en teoría, pero requeriría que cada backend de trazado que no sea de matplotlib implemente esencialmente su propia capa de conversión de matplotlib con una larga cola de incompatibilidades que esencialmente nunca estarán completas (hablando por experiencia como alguien que intentó crear mpld3 algunos años atrás).

y también fija Matplotlib incluso más de lo que ya somos en cuanto a API. Creo que tiene sentido que los pandas declaren qué botones de estilo quieren exponer y esperan que las implementaciones de backend resuelvan lo que eso significa. Esto puede significar _no_ pasar ciegamente **kwargs y, en su lugar, asegurarse de que los objetos devueltos sean "lo correcto" para que el backend dado pueda personalizar el estilo después de los hechos.

Por ejemplo, matplotlib es realmente bueno en gráficos estáticos, pero no muy bien en la producción de gráficos interactivos portátiles.

Gracias @jakevdp , sí, admitir gráficos interactivos es un buen objetivo.

Antes de que las cosas vayan demasiado lejos por esta avenida en particular, aquí hay una solución alternativa.

En lugar de proclamar que la API de trazado de pandas ahora es una especificación y pedir a los paquetes de visualización que la implementen específicamente, ¿por qué no generar una representación intermedia (como un archivo JSON vega) de la trama y alentar a los backends a apuntar a eso como su entrada?

Las ventajas incluyen:

  1. No estar atado al poder expresivo de una API de pandas cosificada, que no fue diseñada como una especificación.
  2. El trabajo realizado al trazar paquetes para admitir pandas, está disponible para otros paquetes de pydata que generan IR.
  3. Promoción de un lenguaje común para la visualización del intercambio en el espacio pydata.
  4. Lo que hace que la nueva herramienta sea más poderosa porque tiene una aplicación más amplia
  5. Lo que hace que el esfuerzo de escribirlos sea más razonable. Básicamente, incentivos mejorados.

Vega / Vega-lite , como un lenguaje de especificación de visualización moderno, establecido, abierto y basado en JSON, varios años-hombre lo pusieron en su diseño e implementación, y las herramientas existentes construidas a su alrededor, parece que fue creado expresamente para este propósito. . (solo por favor no lo hagas ).

Ya sabes, frontend->IR->backend , como están diseñados los compiladores.

Al menos tres paquetes ya implementan la API. Todo lo que los pandas deben hacer es ofrecer una opción para cambiar el backend y documentar su uso, lo que parece una buena inversión por nuestro dinero.

El 15 de junio de 2019, a las 16:28, pilkibun [email protected] escribió:

Por ejemplo, matplotlib es realmente bueno en gráficos estáticos, pero no muy bien en la producción de gráficos interactivos portátiles.

Gracias @jakevdp , sí, admitir gráficos interactivos es un buen objetivo.

Antes de que las cosas vayan demasiado lejos por esta avenida en particular, aquí hay una solución alternativa.

En lugar de proclamar que la API de trazado de pandas ahora es una especificación y pedir a los paquetes de visualización que la implementen específicamente, ¿por qué no generar una representación intermedia (como un archivo JSON vega) de la trama y alentar a los backends a apuntar a eso como su entrada?

Las ventajas incluyen:

No estar atado al poder expresivo de una API de pandas cosificada, que no fue diseñada como una especificación.
El trabajo realizado al trazar paquetes para admitir pandas, está disponible para otros paquetes de pydata que generan IR.
Promoción de un lenguaje común para la visualización del intercambio en el espacio pydata.
Lo que hace que la nueva herramienta sea más poderosa porque tiene una aplicación más amplia
Lo que hace que el esfuerzo de escribirlos sea más razonable. Básicamente, incentivos mejorados.
Vega / Vega-lite, como un lenguaje de especificación de visualización moderno, establecido, abierto y basado en JSON, varios años-hombre lo pusieron en su diseño e implementación, y las herramientas existentes construidas a su alrededor, parece que fue creado expresamente para este propósito. . (solo por favor no lo hagas).

Ya sabes, frontend-> IR-> backend, como están diseñados los compiladores.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub o silencia el hilo.

Ahora fusionamos # 26753, y el backend de trazado se puede cambiar de pandas. Cuando dividimos el código matplotlib, dejamos SeriesPlotMethods y FramePlotMethods en el lado de los pandas (no matplotlib). Eso fue principalmente para dejar las cadenas de documentos en el lado de los pandas.

Pero veo que lo que hicieron los backends fue reimplementar esas clases. Entonces, actualmente esperamos que los backends tengan una clase por trama (por ejemplo, LinePlot , BarPlot ), pero en su lugar implementan una clase con una trama por método (por ejemplo, hvPlot, or the same names as pandas for pdvega `).

Lo que creo que tiene sentido, al menos como primera versión, es que implementamos la API como lo hicieron hvplot y pdvega . Simplemente crearía una clase abstracta en pandas, de la que heredan los backends.

Si eso tiene sentido para todos, comenzaré creando la clase abstracta y adaptando el backend de matplotlib que tenemos en pandas, y una vez hecho esto, adaptamos hvplot y pdvega (los cambios debería ser bastante pequeño).

¿Pensamientos?

Lo que creo que tiene sentido, al menos como primera versión, es que implementamos la API como lo hicieron hvplot y pdvega. Simplemente crearía una clase abstracta en pandas, de la que heredan los backends.

Creo que, a fin de cuentas, este enfoque será más limpio. No puedo hablar con otros backends de trazado, pero al menos en hvPlot diferentes métodos de trazado comparten bastante código, por ejemplo, scatter , line y area son en gran medida análogos, y Preferiría no depender de las subclases para compartir código entre ellos. Además, creo que los diferentes backends deberían tener la opción de agregar tipos de gráficos adicionales y exponerlos como métodos públicos adicionales parece ser el enfoque más simple y natural.

Solo para asegurarme de que entiendo, cuando dices I'd prefer not to rely on subclassing to share code between them te refieres a class LinePlot(MPLPlot) , ¿verdad? ¿Y no es que creas que es una mala idea heredar de una clase base abstracta?

Creo que soy +1 en permitir que los backends definan los tipos de tramas que no están en pandas. Pero probablemente no lo implemente ahora. Estamos planeando lanzar pandas en aproximadamente una semana. Y creo que esto requerirá pensar un poco más que llamar a ciegas los métodos de backends si el usuario proporciona kind='foo' y el backend proporciona el método foo (por ejemplo, validación de parámetros, o causará que algunos kind estarán en la documentación y otros no).

Solo para asegurarme de que lo entiendo, cuando dices que prefiero no depender de las subclases para compartir código entre ellos, te refieres como en la clase LinePlot (MPLPlot), ¿verdad? ¿Y no es que creas que es una mala idea heredar de una clase base abstracta?

Sí, eso es correcto. Más concretamente, preferiría no tener que hacer este tipo de cosas:

class MPL1dPlot(MPLPlot):

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

class LinePlot(MPL1dPlot):
    ...

class AreaPlot(MPL1dPlot):
    ...

Lo siento si eso no quedó claro.

Muy a favor de una API más simple que se expone públicamente como la función única en lugar de las clases como ahora se propone en https://github.com/pandas-dev/pandas/pull/27009.

Pregunta / comentario general sobre cómo funciona ahora la opción de backend. Supongamos que soy el desarrollador pdvega y hago que este backend esté disponible. Eso significa que si los usuarios hacen pd.options.plotting.backend = 'pdvega' , ¿la biblioteca pdvega necesita tener una función plot nivel superior?
1) como autor de una biblioteca, esa no es necesariamente la función que desea exponer públicamente (es decir, para el método plot nivel superior desde el punto de vista de la biblioteca, no es necesariamente la API que desea que sus usuarios para usar directamente) y 2) para este caso, es posible que desee poder hacer pd.options.plotting.backend = 'altair' ? (en caso de que los desarrolladores de Altair estén de acuerdo con eso)
Entonces, básicamente, mi pregunta es: ¿es necesario que haya un mapeo exacto 1: 1 en el nombre del backend y qué se importa? (que ahora es necesario ya que simplemente realiza una importación de esa cadena de backend proporcionada).

EDITAR: Veo que en realidad se discutió algo similar en el PR # 26753

Si tomamos la decisión de que los pandas no saben / limitan qué backends se pueden usar (lo cual estoy totalmente a favor de hacer), debemos decidir cómo / qué llamar a los backends.

Lo que se ha implementado y propuesto en el PR en el que estoy trabajando es que la opción plotting.backend es un módulo (puede ser pdvega , altair , altair.pandas , o lo que sea), y ese módulo debe tener una función pública plot , que es lo que llamaremos.

Podemos considerar otras opciones, como si la opción es pdvega , importamos pdvega.pandas , o podemos nombrar la función plot_pandas o lo que sea. Creo que la forma propuesta es la más sencilla, pero si hay otras propuestas que tengan más sentido, me alegra cambiarla.

Otra discusión es si queremos forzar a los usuarios a importar los backends manualmente:

import pandas
import hvplot

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

Si hacemos eso, los módulos pueden registrarse por sí mismos, también pueden registrar alias (por lo que set_option puede entender otros nombres además del nombre del módulo). También pueden implementar funciones personalizadas o maquinaria (por ejemplo, administradores de contexto) para trazar con ciertos backends, ... Personalmente, creo que cuanto más simples mantengamos las cosas, mejor.

Y aunque podría ser bueno hacer pandas.set_option('plotting.backend', 'bokeh') para trazar en bokeh, creo que eso implica dos cosas que personalmente no me gustan:

  • pandas.set_option('plotting.backend', 'bokeh') solo funcionará si se ha llamado import pandas_bokeh y será confuso para los usuarios.
  • También implica que solo hay un módulo para trazar en bokeh . Lo cual no tiene por qué ser cierto, y da la impresión equivocada a los usuarios de que está trazando directamente con bokeh, y no con pandas trazando backend para bokeh.

@datapythonista gracias por la respuesta detallada. Estoy de acuerdo con mantenerlo ahora como está para la versión inicial (la posibilidad de un alias siempre se puede agregar más adelante).

Si los usuarios quieren la gráfica de la curva de Andrew de hvplot, deben importar la función de hvplot y pasar el marco de datos allí.

+1, tampoco expondría todas las funciones de trazado adicionales a través del backend.

Pero acerca de moverlos a pandas.plotting.matplotlib , me parece una ruptura innecesaria e incompatible hacia atrás (asumiendo que no solo te refieres a mover la implementación).

pandas.set_option ('plotting.backend', 'bokeh') solo funcionará si se ha llamado a import pandas_bokeh, y será confuso para los usuarios.

Si usamos puntos de

Además, por lo que vale, una vez que esto entra, creo que probablemente desaprobaría pdvega y movería el código relevante a un nuevo paquete llamado pandas_altair o algo similar.

@datapythonista Creo que deberíamos decidir sobre el alcance de la API de backend de trazado antes de 0.25.0 (aunque no para el RC).

¿Está a favor de mantener las funciones de trazado misceláneas (así como hist / boxplot)?

@datapythonista cerrar esto cuando fusionamos el PR?

@jreback Mantendría esto abierto hasta que estemos de acuerdo con la API, @TomAugspurger y @jorisvandenbossche no querían delegar en el backend nada excepto los gráficos de acceso.

Lo que haría por los pandas que trazan: el backend es el siguiente.

Para el lanzamiento:

  • Deje las cosas como están, hvplot implementa todo, todas las parcelas, las de los accesores y las que no. Y creo que delegar todo hace que las cosas sean simples.
  • No estoy seguro si excluiría de lo anterior los register_converters. Al menos deberíamos cambiar el nombre de register_matplotlib_converters si los delegamos

Para el próximo lanzamiento:

  • Desaprobaría todos los duplicados pandas.plotting.boxplot , Series.hist , ...
  • Movería todas las parcelas que se llamarán desde los descriptores de acceso (andrew_curves, radviz, parallel_curves, ...).

Para una versión inicial de la API de backend, preferiría ser más conservador en lo que exponemos, en lugar de incluir todo. Es mucho más fácil agregar cosas más tarde que quitarlas.

Personalmente, tampoco movería todos esos gráficos misceláneos al descriptor de acceso (puede haber algunas excepciones, como la matriz de dispersión), en mi opinión, andrew_curves y radviz, etc. no "valen" un método.

Dicho esto: ¿queremos permitir que los backends implementen "tipos" adicionales? Por lo tanto, no tenemos que decidir, como pandas, exactamente qué métodos de acceso pueden estar disponibles. Si el usuario pasa un determinado kind o intenta acceder a un atributo, aún podríamos pasarlo al backend plot con un __getattribute__ .

Solo para explicar un poco por qué las cosas son como son ahora. Es relevante porque no estoy muy seguro de cómo implementar los cambios que propones, o no exponer las cosas en general. No digo aquí que no se pueda hacer de otra manera, es solo para enriquecer la discusión.

La primera decisión fue mover todo el código usando matplotlib a un módulo separado ( pandas.plotting._matplotlib ). Al hacer eso, ese módulo de alguna manera se convirtió en el backend de matplotlib.

Todo lo que era público en pandas.plotting se ha mantenido como público allí. Y para hacer las cosas lo más simples posible, cada una de estas funciones, una vez llamadas, carga el backend (llamada a _get_plot_backend ) y llama a la función allí.

La API pública para el usuario no ha cambiado en absoluto, los usuarios todavía tienen los mismos métodos y funciones disponibles. No estamos exponiendo nada nuevo.

Cómo entiendo las cosas, si decidimos que un gráfico existente como andrew_curves no se delega al backend, lo que esto implica es que en lugar de que el usuario seleccione el backend, seguiremos seleccionando el backend matplotlib. Dado que al menos hvplot ya está implementando andrew_curves , personalmente no veo el punto. Si el usuario quiere un gráfico andrew_curves en matplotlib es tan fácil como no cambiar el backend (o configurarlo de nuevo si se ha cambiado). Entonces, con el cambio, lo que haríamos es simplemente hacer la vida de los usuarios mucho más difícil, agregando complejidad adicional a los pandas.

Si queremos ser amables con los desarrolladores de backend y no obligarlos a implementar gráficos que pueden no ser tan comunes (¿supongo que ese es uno de los razonamientos?), Puede ser que podamos usar por defecto el backend de matplotlib todo lo que falta en el backend seleccionado. ?

Acerca de delegar cualquier tipo de trama desconocida al backend, estoy -1 en hacerlo ahora mismo. Seguramente eventualmente tendrá sentido. Pero creo que tener varios tipos de tramas documentados en pandas, y tener otros adicionales que no documentamos, se siente un poco extraño. Creo que puede esperar a la próxima versión, después de que tengamos comentarios sobre cómo funcionan los diferentes backends para los usuarios, y tengamos más tiempo para discutir y analizar en detalle.

Si el usuario quiere un diagrama de andrew_curves en matplotlib es tan fácil como no cambiar el backend (o configurarlo de nuevo si se ha cambiado). Entonces, con el cambio, lo que haríamos es simplemente hacer la vida de los usuarios mucho más difícil, agregando complejidad adicional a los pandas.

No creo que le hagamos la vida más difícil al usuario. En lugar de importarlo desde pandas.plotting, si quieren una versión de hvplot, simplemente pueden importarlo desde allí. Lo cual es algo que no es posible para el método DataFrame.plot, ya que está definido en el objeto. Para mí, esa es la razón principal del backend de trazado.

Si queremos ser amables con los desarrolladores de backend y no obligarlos a implementar gráficos que pueden no ser tan convencionales

Para mí, no se trata de ser amable o de que se requiera implementar todo (está totalmente bien si un backend no admite todos los tipos de trazado, en mi opinión), sino de una expansión innecesaria de la API del backend de trazado, que también nos ata a él. .
Si reiniciamos pandas desde cero, no creo que se incluyan esos tipos de trazado misceláneos. Pero con la API de backend de trazado, de alguna manera estamos comenzando algo nuevo.

¿Alguna otra opinión sobre esto?

De acuerdo con @jorisvandenbossche.


Sólo para asegurarse de que esto no se pierde, creo @jakevdp 's sugerencia de los puntos de entrada de uso setuptool la pena considerar para resolver el problema de registro para la importación: https://github.com/pandas-dev/pandas/issues/26747 #issuecomment -507415929

@jorisvandenbossche ¿cómo cambiarías eso en el código? En lugar de obtener el backend definido en la configuración de esos métodos, ¿obtener el backend de matplotlib? Creo que esto está mal conceptualmente, pero estoy de acuerdo si hay acuerdo. Cualquier cosa que revierta el desacoplamiento del código matplotlib del resto, soy -1.

Ya que mencionas que en un pandas desde cero no incluiríamos esas parcelas, ¿deberíamos desaprobarlas? Estoy +1 en mover todas las parcelas que no son métodos de Series o DataFrame a un paquete de terceros. O si alguno es lo suficientemente importante como para mantenerse, moverlo para que se llame con .plot() como los demás.

desaprobaría las parcelas no estándar en pandas
y pasar a un paquete externo

Joris está desconectado por un tiempo.

Creo que cuando hemos hablado de esto en el pasado, su posición y la mía sobre las tesis es simplemente dejarlas intactas hasta que se conviertan en una carga de mantenimiento.

Solo para que estemos en la misma página, este es un resumen de lo que tenemos y mi comprensión del estado de la discusión:

Usados ​​como métodos de Series y DataFrame (afaik, estamos felices de mantenerlos como están, delegados al backend seleccionado):

  • PlotAccessor
  • boxplot_frame
  • boxplot_frame_groupby
  • hist_frame
  • hist_series

Otros gráficos (en discusión si deberían estar obsoletos, delegados al backend de matplotlib o delegados al backend seleccionado):

  • diagrama de caja
  • scatter_matrix
  • radviz
  • andrews_curves
  • bootstrap_plot
  • coordenadas_paralelas
  • lag_plot
  • autocorrelation_plot
  • mesa

Otras cosas públicas en pandas.plotting (también en discusión):

  • plot_params
  • register_matplotlib_converters
  • deregister_matplotlib_converters

Para la sección Other plots , personalmente creo que son una carga de mantenimiento en este punto, y estoy +1 en sacarlos de los pandas, y los desapruebo en 0.25.

Para los convertidores y las otras cosas, lo que tenemos ahora seguramente no es correcto, ya que register_matplotlib_converters delega en la trama seleccionada, que no puede ser matplotlib. Las opciones que supongo que podemos considerar son:

  • Cámbieles el nombre a register_converters / deregister_converters , desapruebe los actuales y siga delegando al backend
  • Muévalos de pandas.plotting a pandas.plotting.matplotlib (lo que implicaría hacer público el backend de matplotlib, así que yo no lo haría)
  • Déjelos como están y delegue en el backend de matplotlib en lugar del backend seleccionado (veo esto más como un truco que como una buena decisión de diseño, prefiero mantener pandas.plotting independiente de los backend que existen)

Para la sección Otras parcelas, personalmente creo que son una carga de mantenimiento en este punto, y estoy +1 en sacarlas de los pandas, y las desapruebo en 0.25.

¿Cómo cree que las "otras parcelas" son una carga de mantenimiento? Mirando el historial de las parcelas "misceláneas": https://github.com/pandas-dev/pandas/commits/0.24.x/pandas/plotting/_misc.py , tenemos ~ 10-15 confirmaciones desde 2017. La la mayoría son limpiezas globales aplicadas a todo el código base (por lo que es una pequeña carga marginal). Solo veo 1-2 confirmaciones que cambian los documentos y ninguna confirmación cambia la funcionalidad.

Cambie el nombre a register_converters / deregister_converters, desapruebe los actuales y siga delegando al backend

No creo que esto tenga sentido. Hay convertidores específicos de matplotlib que hemos escrito para matplotlib. Otros backends no los tendrán. Probablemente no debería ser parte de la API de backend.

No quise decir que esas parcelas sean una carga por la cantidad de mantenimiento que hemos tenido en los últimos meses de años, sino por el problema que suponen ahora de tener una API consistente e intuitiva para los usuarios, y una buena estructura modular. diseño de código para nosotros.

Con respecto a los convertidores, no sé si los autores de backend pueden querer implementar el equivalente de los de matplotlib en algunos casos. Pero no parece un problema si no lo hacen, y esas funciones no hacen nada para algunos o todos los demás backends. También estoy de acuerdo con la opción 2, pero no la encuentro tan ordenada.

pero por el problema que suponen ahora en tener una API consistente e intuitiva para los usuarios, y un buen diseño de código modular para nosotros.

Sin embargo, ya son algo inconsistentes con DataFrame.plot. El nombre "misc" implica que :) ¿Tener un backend intercambiable lo empeora? ¿En la medida en que vale la pena perder el código de usuario? No lo creo.

No sé si los autores de backend pueden querer implementar el equivalente de los de matplotlib en algunos casos.

No lo creo. El objetivo de esos convertidores es enseñar a matplotlib sobre los objetos pandas. Las bibliotecas que implementan el backend no tendrán ese problema, ya que dependen de pandas.

Personalmente, lo pienso principalmente en términos de gestión de la complejidad. Tener una API de trazado estándar que se delega al backend a través de una única API es fácil de entender y mantener. Los usuarios y mantenedores solo necesitan aprender que hay una función plot con un argumento kind , y que esto se ejecutará en el backend seleccionado.

Tener en el backend un conjunto de parcelas heterogéneas, que además de no seguir la misma API, usan un backend, pero no el seleccionado para las otras parcelas, sino el de Matplotlib, agrega demasiada complejidad para todos en mi humilde opinión.

Y el costo de moverlos me parece pequeño, supongo que no una gran proporción de nuestros usuarios ni siquiera conocen esas parcelas. Y para los que lo hacen, solo necesitarán instalar un paquete conda adicional y usar import pandas_plotting; pandas_plotting.andrews_curves(df) lugar de pandas.plotting.andrews_curves(df) .

A mí me parece mucho por ganar, a un pequeño costo, pero por supuesto es solo una opinión.

¿Podemos documentar que el backend intercambiable es solo para Series / DataFrame.plot? Parece una regla bastante simple.

Se siente como un truco que me agrega una complejidad innecesaria; No creo que explicarlo en la documentación lo haga menos contradictorio.

Pero de todos modos, no es gran cosa. Si esa es la opción preferida, así es como la implementaría, al menos el aumento en la complejidad del código es mínimo: # 27432

Mirando esto más de cerca ahora: si entiendo correctamente, la forma en que se configurará el backend de trazado es usando:

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

Tengo entendido, entonces, que si quiero hacer el siguiente trabajo:

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

entonces necesitaré el paquete altair de nivel superior para definir todas las funciones en https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py. Preferiría no contaminar el espacio de nombres de nivel superior de Altair con todas estas API adicionales que no están destinadas a ser utilizadas por los usuarios de Altair. De hecho, preferiría que la extensión de pandas de altair viva en un paquete separado, por lo que no está vinculado a la cadencia de lanzamiento de Altair en sí.

Si entiendo correctamente, esto significa que no hay forma de que haga que pd.set_option('plotting.backend', 'altair') funcione correctamente sin codificar el paquete altair en pandas de la forma en que matplotlib está codificado actualmente, ¿es correcto?

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

Si es así, recomiendo encarecidamente repensar los medios por los cuales esta API se expone en paquetes de terceros.

Mi solución sugerida sería adoptar un marco basado en puntos de entrada que me permitiera, por ejemplo, crear un paquete como altair_pandas que registre el punto de entrada altair para implementar la API. De lo contrario, los usuarios siempre estarán confundidos de que pd.set_option('plotting.backend', 'altair') no hace lo que esperan.

Acordado. Creo que los puntos de entrada son el camino a seguir. Haré un prototipo de algo.

El viernes 19 de julio de 2019 a la 1:16 p. M. Jake Vanderplas [email protected]
escribió:

Mirando esto más de cerca ahora: si entiendo correctamente, la forma en que
el backend de trazado se configurará usando:

pd.set_option ('plotting.backend', 'nombre_del_módulo')

Tengo entendido, entonces, que si quiero hacer el siguiente trabajo:

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

entonces necesitaré el paquete altair de nivel superior para definir todas las funciones
en
https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py.
Preferiría no contaminar el espacio de nombres de nivel superior de Altair con todos estos
API adicionales. De hecho, preferiría que la extensión de pandas de altair
vivir en un paquete separado, por lo que no está vinculado a la cadencia de lanzamiento de
Altair en sí.

Si entiendo correctamente, esto significa que no hay forma de que pueda hacer pd.set_option ('plotting.backend',
'altair') funcionan correctamente sin codificar el paquete altair en pandas
la forma en que matplotlib está codificado actualmente, ¿es correcto?

Si es así, recomiendo encarecidamente repensar cómo se habilita esto mediante
paquetes de terceros. En particular, adoptar un marco basado en puntos de entrada
me dejaría crear un paquete como altair_pandas que registra el altair
punto de entrada. De lo contrario, los usuarios siempre estarán confundidos porque pd.set_option ('plotting.backend',
'altair') no hace lo que esperan.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOITQM7HH5X4SZ4IAPS3QAIAIBA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXWH5WZVMV ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAKAOISFLHDGXLGQ3PUMNLDQAIAIBANCNFSM4HWIMEKQ
.

Hubo un momento en el que lo que dices era en su mayoría correcto, pero ese ya no es el caso.

Si quieres pandas.options.plotting.backend = 'altair' , en 0.25 solo necesitas tener una función altair.plot() . En algún momento pensé que sería mejor llamar a la función pandas_plot lugar de simplemente plot , por lo que era específico en un backend que tenía otras cosas, pero finalmente no hicimos el cambio.

Si crear la función plot en el nivel superior de altair es un problema, podemos cambiarle el nombre en una versión futura, o también puede tener altair.pandas.plot , pero los usuarios tendrán que configurar pandas.options.plotting.backend = 'altair.pandas' .

Seguramente puede cambiar la opción usted mismo una vez que los usuarios hagan un import altair . Y podríamos implementar un registro de backends. Pero creo que sería confuso para los usuarios si hacen el pandas.options.plotting.backend = 'altair' y falla, porque olvidaron el import altair antes.

Una última cosa es considerar que posiblemente podríamos tener más de un backend de pandas implementado para altair (o cualquier otra biblioteca de visualización). Entonces, para mí, que el nombre del backend no sea altair , no es necesariamente algo malo.

Aquí hay una implementación basada en puntos de entrada.

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
 )

Creo que es bastante agradable. Los paquetes de terceros modificarán su setup.py (o pyproject.toml) para incluir algo como

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

Me gusta que rompe el estrecho vínculo entre naming e implementación.

No trabajé con puntos de entrada, ¿son como un registro global del entorno Python? Siendo nuevo para ellos, no me encanta la idea, pero supongo que sería una forma razonable de hacerlo en ese momento.

Todavía me gustaría tener ambas opciones, por lo que si el usuario hace pandas.options.plottting.backend = 'my_own_project.my_custom_small_backend' , funciona y no requiere la creación de un paquete y la configuración de puntos de entrada.

No trabajé con puntos de entrada, ¿son como un registro global del entorno Python?

Yo tampoco los he usado, pero creo que esa es la idea. Por lo que tengo entendido, son de setuptools (¿pero paquetes como flit hook en ellos?). Por lo tanto, no forman parte de la biblioteca estándar, pero las herramientas de configuración son lo que todos usan de todos modos.

Todavía me gustaría tener ambas opciones

Volver a import_module(backend_name) parece razonable.

¿Fue útil esta página
0 / 5 - 0 calificaciones