Pandas: API: definir API para pandas que traçam back-ends

Criado em 9 jun. 2019  ·  44Comentários  ·  Fonte: pandas-dev/pandas

No # 26414, dividimos o módulo de plotagem de pandas em uma estrutura de plotagem geral capaz de chamar diferentes back-ends e os back-ends matplotlib atuais. A ideia é que outros back-ends possam ser implementados de forma mais simples, e ser usados ​​com uma API comum pelos usuários do pandas.

A API definida pelo backend matplotlib atual inclui os objetos listados a seguir, mas essa API provavelmente pode ser simplificada. Aqui está a lista de perguntas / propostas:

Métodos não controversos para manter na API (eles fornecem a funcionalidade Series.plot(kind='line') ...):

  • LinePlot
  • BarPlot
  • BarhPlot
  • HistPlot
  • BoxPlot
  • KdePlot
  • AreaPlot
  • PiePlot
  • ScatterPlot
  • HexBinPlot

Funções de plotagem fornecidas nos pandas (por exemplo, pandas.plotting.andrews_curves(df) )

  • andrews_curves
  • autocorrelation_plot
  • bootstrap_plot
  • lag_plot
  • paralelo_coordenadas
  • Radviz
  • scatter_matrix
  • tabela

Devem fazer parte da API e outros back-ends também devem implementá-los? Faria sentido converter para o formato .plot (por exemplo, DataFrame.plot(kind='autocorrelation') ...)? Faz sentido ficar fora da API ou mudar para um módulo de terceiros?

Métodos redundantes que podem ser removidos:

  • hist_series
  • hist_frame
  • boxplot
  • boxplot_frame
  • boxplot_frame_groupby

No caso de boxplot , atualmente temos várias maneiras de gerar um gráfico (chamando principalmente o mesmo código):

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

Pessoalmente, eu descontinuaria o número 4 e, para o número 3, descontinuaria ou pelo menos não exigiria um método boxplot_frame separado no back-end, mas tentaria reutilizar BoxPlot (para comentários do número 3, o mesmo aplica-se a hist ).

Para boxplot_frame_groupby , não verifiquei em detalhes, mas não tem certeza se BoxPlot poderia ser reutilizado para isso?

Funções para registrar conversores:

  • registro
  • cancelar o registro

Isso faz sentido para outros back-ends?

Obsoleto no pandas 0,23, a ser removido:

  • tsplot

Para ver o que cada uma dessas funções faz na prática, pode ser útil este bloco de notas por @liirusuk : https://github.com/python-sprints/pandas_plotting_library/blob/master/AllPlottingExamples.ipynb

CC: @ pandas-dev / pandas-núcleo @tacaswell, @jakevdp, @philippjfr, @PatrikHlobil

API Design Clean Needs Discussion Visualization

Comentários muito úteis

Aqui está uma implementação baseada em pontos 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
 )

Eu acho que é muito bom. Pacotes de terceiros modificarão seu setup.py (ou pyproject.toml) para incluir algo como

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

Gosto de quebrar o estreito acoplamento entre nomenclatura e implementação.

Todos 44 comentários

Acho que manter coisas como autocorrelação fora da API de back-end substituível.

Acho que deixamos coisas como df.boxplot e hist porque eles têm um comportamento ligeiramente diferente do da API .plot. Eu não recomendaria torná-los parte da API de back-end.

Aqui está o meu início em uma API de back-end proposta há alguns meses: https://github.com/TomAugspurger/pandas/commit/b07aba28a37b0291fd96a1f571848a7be2b6de8d

Acho que vale a pena mencionar que pelo menos hvplot (não checou o resto) já fornece as funções como andrews_curves , scatter_matrix , lag_plot ,. ..

Pode ser que, se não quisermos forçar todos os back-ends a implementá-los, podemos verificar se o back-end selecionado os implementa e usar como padrão os gráficos matplotlib.

Presumi que boxplot e hist comportavam exatamente da mesma forma, mas apenas tinham os atalhos Series.hist() para Series.plot.hist() . O "atalho" mostra a grade do gráfico, mas além disso não vi nenhuma diferença.

IMO, o valor principal desta opção é o namespace .plot .

Se os usuários quiserem o gráfico da curva de Andrew do hvplot, eles devem importar a função
do hvplot e passar o dataframe lá.

No domingo, 9 de junho de 2019 às 7h17, Marc Garcia [email protected] escreveu:

Acho que vale a pena mencionar que pelo menos hvplot (não checou o
rest) já fornece funções como andrews_curves,
scatter_matrix, lag_plot, ...

Pode ser, se não quisermos forçar todos os back-ends a implementá-los, podemos
verifique se o back-end selecionado os implementa e o padrão para o
plotagens de matplotlib?

Eu presumi que o boxplot e o hist se comportavam exatamente da mesma forma, mas apenas
atalhos Series.hist () para Series.plot.hist (). O "atalho" mostra o
grade de enredo, mas fora isso eu não vi nenhuma diferença.

-
Você está recebendo isso porque faz parte de uma equipe que foi mencionada.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOIRLJHBMXMXKK2IG2NDPZTYFPA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXII77Y#issuecomment-500207615 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AAKAOISDHL6H7PVOOJAQXELPZTYFPANCNFSM4HWIMEKQ
.

Acho que faz sentido, mas se fizermos isso, acho que devemos movê-los para pandas.plotting.matplotlib.andrews_curves , em vez de pandas.plotting.andrews_curves .

@TomAugspurger Preciso verificar com mais detalhes, mas acho que a API que você implementou em https://github.com/TomAugspurger/pandas/commit/b07aba28a37b0291fd96a1f571848a7be2b6de8d é a que faz mais sentido. Vou trabalhar nisso assim que terminar o # 26753. Também vou experimentar se é viável mover andrews_curves , scatter_matrix ... para a sintaxe .plot() , acho que isso tornará as coisas mais simples e fáceis para todos (nós , bibliotecas de terceiros e usuários).

Qual é a intenção aqui com relação a kwargs extras passados ​​para funções de plotagem? Os back-ends adicionais devem tentar duplicar a funcionalidade de todas as personalizações de plotagem no estilo matplotlib ou devem permitir a transmissão de palavras-chave que correspondam às usadas pelo back-end específico?

A primeira opção seria boa em teoria, mas exigiria que cada back-end de plotagem não-matplotlib implementasse essencialmente sua própria camada de conversão matplotlib com uma longa cauda de incompatibilidades que essencialmente nunca seriam completas (falando por experiência como alguém que tentou criar algum mpld3 anos atrás).

A segunda opção não é tão boa do ponto de vista da intercambialidade, mas permitiria que outros back-ends fossem adicionados com um conjunto de expectativas mais razoável.

Acho que depende do back-end o que eles fazem com eles. Atingindo 100%
compatibilidade entre back-ends não é realmente viável,
já que o tipo de retorno não será mais um matplotlib Axes. E se
não somos compatíveis no tipo de retorno, não acho que os back-ends
deve se dobrar para trás para tentar lidar com todos os argumentos de palavra-chave possíveis.

Então eu acho que os pandas devem documentar que **kwargs será repassado para
o mecanismo de plotagem subjacente, e eles podem fazer o que quiserem com
eles.

Na segunda-feira, 10 de junho de 2019 às 10:42 Jake Vanderplas [email protected]
escreveu:

Qual é a intenção aqui com relação a kwargs extras passados ​​para a plotagem
funções? Se back-ends adicionais tentarem duplicar o
funcionalidade de todas as personalizações de trama no estilo matplotlib, ou devem
permitem que palavras-chave sejam passadas que correspondam àquelas usadas por um determinado
Processo interno?

A primeira opção seria boa em teoria, mas exigiria todos os
back-end de plotagem não-matplotlib para essencialmente implementar seu próprio matplotlib
camada de conversão com uma longa cauda de incompatibilidades que
essencialmente nunca ser completo (falando por experiência própria como alguém que
tentei criar mpld3 alguns anos atrás).

A segunda opção não é tão boa do ponto de vista de
intercambialidade, mas permitiria que outros back-ends fossem adicionados com mais
conjunto razoável de expectativas.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOIS3IBV4XSSY7BPSCF3PZZY5LA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXKH4AY#issuecomment-500465155 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AAKAOIQ3GYOGAPUZ4LSNK2DPZZY5LANCNFSM4HWIMEKQ
.

Lamento se esta é uma pergunta estúpida, mas se você definir uma "API" de plotagem que é basicamente um grupo de plotagens prontas, todo backend não produziria mais ou menos a mesma saída? que novo recurso isso deve permitir? algo como um exportador de pandas para vega, talvez?

Não acho correto dizer que todo back-end produz mais ou menos a mesma saída.

Por exemplo, matplotlib é realmente bom em gráficos estáticos, mas não é ótimo em produzir gráficos interativos portáteis.

Por outro lado, bokeh, altair, et al. são ótimos para gráficos interativos, mas não são tão maduros quanto matplotlib para gráficos estáticos.

Ser capaz de produzir ambos com a mesma API seria uma grande vitória.

A primeira opção seria boa em teoria, mas exigiria que cada back-end de plotagem não-matplotlib implementasse essencialmente sua própria camada de conversão matplotlib com uma longa cauda de incompatibilidades que essencialmente nunca seriam completas (falando por experiência como alguém que tentou criar algum mpld3 anos atrás).

e também fixa o Matplotlib ainda mais do que já conhecemos a API. Eu acho que faz sentido para o pandas declarar quais botões de estilo ele deseja expor e esperar que as implementações de back-end resolvam o que isso significa. Isso pode significar _não_ passar **kwargs cegamente e, em vez disso, garantir que os objetos retornados sejam "a coisa certa" para que o back-end fornecido seja capaz de fazer uma personalização de estilo após o fato.

Por exemplo, matplotlib é realmente bom em gráficos estáticos, mas não é ótimo em produzir gráficos interativos portáteis.

Obrigado @jakevdp , sim, apoiar gráficos interativos é uma boa meta.

Antes que as coisas vão longe demais nesta avenida em particular, aqui está uma solução alternativa.

Em vez de proclamar que a API de plotagem do pandas agora é uma especificação e pedir aos pacotes viz para implementá-la especificamente, por que não gerar uma representação intermediária (como um arquivo JSON vega) da plotagem e encorajar back-ends a direcioná-la como sua entrada.

As vantagens incluem:

  1. Não estar amarrado ao poder expressivo de uma API de pandas reificada, que não foi projetada como uma especificação.
  2. O trabalho feito pela plotagem de pacotes para suportar pandas, torna-se disponível para outros pacotes pydata que geram IR.
  3. Promover uma linguagem comum para visualização de intercâmbio no espaço pydata
  4. O que torna a nova ferramenta mais poderosa porque é mais amplamente aplicável
  5. O que torna o esforço de escrevê-los mais razoável. Basicamente, incentivos aprimorados.

Vega / Vega-lite , como uma linguagem de especificação de viz moderna, estabelecida, aberta e baseada em JSON, vários homens-anos a colocaram em seu design e implementação, e as ferramentas existentes construídas em torno dela, parecem ter sido criadas expressamente para este propósito . (apenas não faça isso ).

Você sabe, frontend->IR->backend , como os compiladores são projetados.

Pelo menos três pacotes já implementam a API. Tudo o que o pandas precisa fazer é oferecer uma opção para alterar o backend e documentar seu uso, o que parece um bom retorno para o nosso investimento.

Em 15 de junho de 2019, às 16:28, pilkibun [email protected] escreveu:

Por exemplo, matplotlib é realmente bom em gráficos estáticos, mas não é ótimo em produzir gráficos interativos portáteis.

Obrigado @jakevdp , sim, apoiar gráficos interativos é uma boa meta.

Antes que as coisas vão longe demais nesta avenida em particular, aqui está uma solução alternativa.

Em vez de proclamar que a API de plotagem do pandas agora é uma especificação e pedir aos pacotes viz para implementá-la especificamente, por que não gerar uma representação intermediária (como um arquivo JSON vega) da plotagem e encorajar back-ends a direcioná-la como sua entrada.

As vantagens incluem:

Não estar amarrado ao poder expressivo de uma API de pandas reificada, que não foi projetada como uma especificação.
O trabalho feito pela plotagem de pacotes para suportar pandas, torna-se disponível para outros pacotes pydata que geram IR.
Promover uma linguagem comum para visualização de intercâmbio no espaço pydata
O que torna a nova ferramenta mais poderosa porque é mais amplamente aplicável
O que torna o esforço de escrevê-los mais razoável. Basicamente, incentivos aprimorados.
Vega / Vega-lite, como uma linguagem de especificação de viz moderna, estabelecida, aberta e baseada em JSON, vários homens-anos a colocaram em seu design e implementação, e as ferramentas existentes construídas em torno dela, parecem ter sido criadas expressamente para este propósito . (apenas não faça isso).

Você sabe, frontend-> IR-> backend, como os compiladores são projetados.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

Agora fundimos # 26753, e o backend de plotagem pode ser alterado dos pandas. Quando dividimos o código matplotlib, deixamos SeriesPlotMethods e FramePlotMethods no lado dos pandas (não matplotlib). Isso era principalmente para deixar os docstrings do lado dos pandas.

Mas vejo que o que os back-ends fizeram foi reimplementar essas classes. Portanto, atualmente esperamos que os back-ends tenham uma classe por gráfico (por exemplo, LinePlot , BarPlot ), mas em vez disso, eles implementam uma classe com um gráfico por método (por exemplo, hvPlot, or the same names as pandas for pdvega `).

O que eu acho que faz sentido, pelo menos como uma primeira versão, é que implementamos a API como hvplot e pdvega fizeram. Eu acabaria de criar uma classe abstrata em pandas, da qual os back-ends herdam.

Se isso fizer sentido para todos, começarei criando a classe abstrata e adaptando o backend matplotlib que temos nos pandas e, uma vez feito isso, adaptamos hvplot e pdvega (as alterações deve ser muito pequeno).

Pensamentos?

O que eu acho que faz sentido, pelo menos como uma primeira versão, é que implementemos a API como o hvplot e o pdvega fizeram. Eu acabaria de criar uma classe abstrata em pandas, da qual os back-ends herdam.

Acho que, no geral, essa abordagem será mais limpa. Não posso falar com outros back-ends de plotagem, mas pelo menos no hvPlot diferentes métodos de plot compartilham um pouco de código, por exemplo, scatter , line e area são amplamente análogos e Eu prefiro não depender de subclasses para compartilhar código entre eles. Além disso, acho que back-ends diferentes devem ter a opção de adicionar tipos de plotagem adicionais e expô-los como métodos públicos adicionais parece a abordagem mais simples e natural.

Só para ter certeza de que entendi, quando você diz I'd prefer not to rely on subclassing to share code between them quer dizer como em class LinePlot(MPLPlot) , certo? E não que você ache uma má ideia herdar de uma classe base abstrata?

Eu acho que estou +1 em permitir que back-ends definam tipos de trama, não em pandas. Mas provavelmente não irei implementá-lo agora. Estamos planejando soltar os pandas em cerca de uma semana. E eu acho que isso exigirá um pouco mais de reflexão do que chamar cegamente os métodos de back-ends se o usuário fornecer kind='foo' e o back-end fornecer o método foo (por exemplo, validação de parâmetro ou causará que alguns kind estarão na documentação e outros não).

Só para ter certeza de que entendi, quando você diz que prefiro não depender de subclasses para compartilhar código entre eles, você quer dizer como na classe LinePlot (MPLPlot), certo? E não que você ache uma má ideia herdar de uma classe base abstrata?

Sim está certo. Mais concretamente, prefiro não ter que fazer este tipo de coisa:

class MPL1dPlot(MPLPlot):

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

class LinePlot(MPL1dPlot):
    ...

class AreaPlot(MPL1dPlot):
    ...

Desculpe se não ficou claro.

Muito a favor de uma API mais simples que seja publicamente exposta como a única função em vez das classes, como agora proposto em https://github.com/pandas-dev/pandas/pull/27009.

Pergunta / observação geral sobre como a opção de back-end agora funciona. Suponha que eu seja o desenvolvedor pdvega e disponibilize este back-end. Isso significa que se os usuários fizerem pd.options.plotting.backend = 'pdvega' , a biblioteca pdvega precisa ter uma função de nível superior plot ?
1) como autor de uma biblioteca, essa não é necessariamente a função que você deseja expor publicamente (ou seja, para o método plot de nível superior do ponto de vista da biblioteca, não é necessariamente a API que você deseja que seus usuários para usar diretamente) e 2) para este caso, você pode realmente querer fazer pd.options.plotting.backend = 'altair' ? (caso os desenvolvedores do Altair concordem com isso)
Então, basicamente, minha pergunta é: é necessário haver um mapeamento 1: 1 exato no nome do back-end e o que é importado? (que agora é necessário, pois ele simplesmente importa a string de back-end fornecida).

EDITAR: vejo que na verdade algo semelhante foi discutido no PR # 26753

Se tomarmos a decisão de que os pandas não sabem / limitam quais back-ends podem ser usados ​​(o que sou fortemente a favor de fazer), precisamos decidir como / o que chamar nos back-ends.

O que foi implementado e proposto no PR no qual estou trabalhando é que a opção plotting.backend é um módulo (pode ser pdvega , altair , altair.pandas , ou qualquer outra coisa), e esse módulo deve ter uma função pública plot , que é o que chamaremos.

Podemos considerar outras opções, como se a opção for pdvega , importamos pdvega.pandas , ou podemos nomear a função plot_pandas ou qualquer outra coisa. Acho que a forma proposta é a mais simples, mas se houver outras propostas que façam mais sentido, fico feliz em alterá-la.

Outra discussão é se queremos forçar os usuários a importar os back-ends manualmente:

import pandas
import hvplot

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

Se fizermos isso, os módulos podem se registrar, eles também podem registrar aliases (então set_option pode entender outros nomes além do nome do módulo). Eles também podem implementar funções ou máquinas personalizadas (por exemplo, gerenciadores de contexto) para traçar com certos back-ends, ... Pessoalmente, acho que quanto mais simples mantemos as coisas, melhor.

E embora possa ser bom fazer pandas.set_option('plotting.backend', 'bokeh') para traçar no bokeh, acho que isso implica duas coisas que eu pessoalmente não gosto:

  • pandas.set_option('plotting.backend', 'bokeh') só funcionará se import pandas_bokeh for chamado e será confuso para os usuários.
  • Também implica que há apenas um módulo para plotar em bokeh . O que não precisa ser verdade e dá a impressão errada aos usuários de que você está plotando diretamente com bokeh, e não com um pandas plotando backend para bokeh.

@datapythonista obrigado pela resposta detalhada. Eu estou bem em mantê-lo agora como está para a versão inicial (a possibilidade de um alias sempre pode ser adicionado posteriormente).

Se os usuários quiserem o gráfico de curva de Andrew do hvplot, eles devem importar a função do hvplot e passar o dataframe para lá.

+1, eu também não exporia todas as funções adicionais de plotagem por meio do back-end.

Mas sobre movê-los para pandas.plotting.matplotlib , isso parece uma interrupção incompatível com versões anteriores desnecessárias para mim (supondo que você não quisesse apenas mover a implementação).

pandas.set_option ('plotting.backend', 'bokeh') só funcionará se importar pandas_bokeh for chamado e será confuso para os usuários.

Se usarmos pontos de

Além disso, pelo que vale a pena, depois que isso for implementado, acho que provavelmente suspenderei o uso do pdvega e moverei o código relevante para um novo pacote denominado pandas_altair ou algo semelhante.

@datapythonista Acho que devemos decidir sobre o escopo da API de back-end de plotagem antes de 0.25.0 (mas não para o RC).

Você é a favor de manter as funções de plotagem diversas (bem como hist / boxplot)?

@datapythonista fechar isso quando

@jreback Eu manteria isso aberto até que concordássemos com a API, @jorisvandenbossche não queriam delegar nada ao backend, exceto os gráficos de acesso.

O que eu faria para os pandas de plotagem - backend é o próximo.

Para o lançamento:

  • Deixando as coisas como estão, hvplot implementa tudo em todos os lotes, os dos acessadores e os que não são. E acho que delegar tudo mantém as coisas simples.
  • Não tenho certeza se eu excluiria os register_converters acima. Devemos, pelo menos, mudar o nome de register_matplotlib_converters se os delegarmos

Para o próximo lançamento:

  • Eu suspenderia todas as duplicatas pandas.plotting.boxplot , Series.hist , ...
  • Eu moveria todos os gráficos a serem chamados de acessadores (andrew_curves, radviz, parallel_curves, ...).

Para uma versão inicial da API de back-end, prefiro ser mais conservador no que expomos, em vez de incluir tudo. É muito mais fácil adicionar coisas depois do que remover.

Eu pessoalmente também não moveria todos esses gráficos diversos para o acessador (pode haver algumas exceções, como matriz de dispersão), IMO, andrew_curves e radviz etc não "valem" um método.

Dito isso: queremos permitir que os back-ends implementem "tipos" adicionais? Portanto, não temos que decidir, como pandas, exatamente quais métodos acessadores podem estar disponíveis. Se o usuário passa um determinado kind ou tenta acessar um atributo, ainda podemos passá-lo para o backend plot com um __getattribute__ .

Só para explicar um pouco por que as coisas são como são agora. É relevante porque não tenho certeza de como implementar as mudanças que você propõe, ou não expor as coisas em geral. Não estou dizendo aqui que não pode ser feito de outra forma, é apenas para enriquecer a discussão.

A primeira decisão foi mover todo o código usando matplotlib para um módulo separado ( pandas.plotting._matplotlib ). Ao fazer isso, esse módulo de alguma forma se tornou o backend matplotlib.

Tudo o que era público em pandas.plotting foi mantido como público lá. E para tornar as coisas o mais simples possível, cada uma dessas funções, uma vez chamadas, carrega o backend (chame _get_plot_backend ) e chama a função lá.

A API pública para o usuário não tem nenhuma mudança, os usuários ainda têm os mesmos métodos e funções disponíveis. Não estamos expondo nada de novo.

Como eu entendo as coisas, se decidirmos que um gráfico existente como andrew_curves não é delegado ao back-end, o que isso implica é que, em vez de obter o back-end selecionado pelo usuário, ainda selecionaremos o back-end matplotlib. Dado que pelo menos hvplot já está implementando andrew_curves , eu pessoalmente não entendo o ponto. Se o usuário deseja um gráfico andrew_curves em matplotlib é tão fácil quanto não alterar o backend (ou configurá-lo novamente se tiver sido alterado). Então, com a mudança, o que faríamos é simplesmente tornar a vida dos usuários muito mais difícil, adicionando complexidade extra aos pandas.

Se quisermos ser simpáticos com os desenvolvedores de back-end e não forçá-los a implementar plotagens que podem não ser tão convencionais (acho que esse é um dos motivos?), Podemos usar como padrão no back-end matplotlib tudo o que está faltando no back-end selecionado ?

Sobre delegar qualquer tipo desconhecido de trama ao backend, estou -1 em fazer isso agora. Certamente pode fazer sentido eventualmente. Mas eu acho que ter vários tipos de enredo documentados em pandas, e ter alguns extras que não documentamos, parece um pouco maluco. Acho que podemos esperar pela próxima versão, depois que tivermos feedback sobre como ter diferentes back-ends funcionam para os usuários, e tivermos mais tempo para discutir e analisar em detalhes.

Se o usuário deseja um gráfico andrew_curves em matplotlib é tão fácil quanto não alterar o backend (ou configurá-lo novamente se tiver sido alterado). Então, com a mudança, o que faríamos é simplesmente tornar a vida dos usuários muito mais difícil, adicionando complexidade extra aos pandas.

Não acho que estaríamos dificultando a vida do usuário. Em vez de importá-lo de pandas.plotting, se quiserem uma versão do hvplot, eles podem simplesmente importá-lo de lá. O que não é possível para o método DataFrame.plot, já que está definido no objeto. Para mim, essa é a principal razão para o back-end de plotagem.

Se quisermos ser simpáticos com os desenvolvedores de back-end e não forçá-los a implementar parcelas que podem não ser tão convencionais

Para mim, não se trata de ser legal ou que implementar tudo seria necessário (está totalmente bem se um backend não suporta todos os tipos de plotagem, IMO), mas sim uma expansão desnecessária da API de backend de plotagem, que também nos vincula a ela .
Se recomeçássemos os pandas do zero, não acho que esses tipos de plotagem diversos seriam incluídos. Mas com a API de back-end de plotagem, estamos de alguma forma iniciando algo novo.

Alguma outra opinião sobre isso?

Acordado com @jorisvandenbossche.


Só para garantir que isso não seja perdido, acho que vale a pena considerar a sugestão de @jakevdp de usar os pontos de entrada do setuptool para resolver o problema de registro do pedido de importação: https://github.com/pandas-dev/pandas/issues/26747 #issuecomment -507415929

@jorisvandenbossche como você mudaria isso no código? Em vez de definir o backend nas configurações desses métodos, obter o backend matplotlib? Acho que isso está errado conceitualmente, mas estou bem com isso se houver acordo. Qualquer coisa que reverta o desacoplamento do código matplotlib do resto sou -1.

Já que você mencionou que em um pandas do zero não incluiríamos essas parcelas, devemos rejeitá-las? Estou com +1 em mover todos os gráficos que não são métodos de Series ou DataFrame para um pacote de terceiros. Ou se algum for importante o suficiente para ser mantido, movê-lo para ser chamado com .plot() como os outros.

Eu descontinuaria os gráficos não padronizados em pandas
e passar para um pacote externo

Joris está offline um pouco.

Acho que, quando discutimos isso no passado, a posição dele e minha sobre as teses é simplesmente deixá-las intocadas até que se tornem um fardo de manutenção.

Da mesma forma que estamos na mesma página, este é um resumo do que temos, e meu entendimento do estado da discussão:

Usados ​​como métodos de Series e DataFrame (afaik, estamos todos felizes em mantê-los como estão, delegados ao back-end selecionado):

  • PlotAccessor
  • boxplot_frame
  • boxplot_frame_groupby
  • hist_frame
  • hist_series

Outros gráficos (em discussão se devem ser descontinuados, delegados ao back-end matplotlib ou delegados ao back-end selecionado):

  • boxplot
  • scatter_matrix
  • Radviz
  • andrews_curves
  • bootstrap_plot
  • paralelo_coordenadas
  • lag_plot
  • autocorrelation_plot
  • tabela

Outras coisas públicas em pandas.plotting (também em discussão):

  • plot_params
  • register_matplotlib_converters
  • deregister_matplotlib_converters

Para a seção Other plots , eu pessoalmente acho que eles são um fardo de manutenção neste ponto, e estou +1 em removê-los dos pandas e descontinuá-los em 0,25.

Para os conversores e outras coisas, o que temos agora certamente não está correto, uma vez que register_matplotlib_converters delega ao gráfico selecionado, que não pode ser matplotlib. As opções que acho que podemos considerar são:

  • Renomeie-os para register_converters / deregister_converters , desative os atuais e continue delegando ao back-end
  • Mova-os de pandas.plotting para pandas.plotting.matplotlib (o que implicaria em tornar o back-end matplotlib público, então eu não faria)
  • Deixe-os como estão e delegue ao back-end matplotlib em vez do back-end selecionado (vejo isso mais como um hack do que uma boa decisão de design, prefiro manter pandas.plotting independente de quais back-ends existem)

Para a seção Outras parcelas, eu pessoalmente acho que eles são um fardo de manutenção neste ponto, e estou +1 em removê-los dos pandas, e desaprová-los em 0,25.

Como você acha que as "outras parcelas" são um fardo de manutenção? Olhando para o histórico das parcelas "misc": https://github.com/pandas-dev/pandas/commits/0.24.x/pandas/plotting/_misc.py , temos cerca de 10-15 commits desde 2017. O a maioria são limpezas globais aplicadas a toda a base de código (portanto, uma pequena carga marginal). Vejo apenas 1-2 commits mudando os documentos, e nenhum commit mudando a funcionalidade.

Renomeie-os para register_converters / deregister_converters, desative os atuais e continue delegando ao back-end

Eu não acho que isso faria sentido. Existem conversores específicos para matplotlib que escrevemos para matplotlib. Outros back-ends não os terão. Provavelmente não deveria fazer parte da API de back-end.

Não quis dizer que esses gráficos sejam um fardo por causa da quantidade de manutenção que tivemos nos últimos meses dos anos, mas sim pelo problema que eles supõem agora em ter uma API consistente e intuitiva para os usuários, e um bom sistema modular design de código para nós.

Em relação aos conversores, não sei se os autores de back-end podem querer implementar o equivalente daqueles para matplotlib em alguns casos. Mas não parece um problema se não o fizerem, e essas funções não fazem nada para alguns ou todos os outros back-ends. Também estou bem com a opção 2, mas não a acho tão legal.

mas pelo problema que eles supõem agora em ter uma API consistente e intuitiva para os usuários, e um bom design de código modular para nós.

Eles já são um tanto inconsistentes com DataFrame.plot, no entanto. O nome "misc" implica que :) Ter um back-end substituível torna isso pior? Na medida em que vale a pena churn no código do usuário? Acho que não.

Não sei se os autores de back-end podem querer implementar o equivalente daqueles para matplotlib em alguns casos.

Acho que não. O objetivo desses conversores é ensinar matplotlib sobre objetos pandas. Bibliotecas que implementam o backend não terão esse problema, pois já dependem do pandas.

Pessoalmente, penso nisso principalmente em termos de gerenciamento da complexidade. Ter uma API de plotagem padrão delegada ao back-end por meio de uma única API é fácil de entender e manter. Usuários e mantenedores precisam apenas aprender que existe uma função plot com um argumento kind , e que isso será executado no backend selecionado.

Ter no backend um conjunto de plotagens heterogêneas, que além de não seguirem a mesma API, utilizam um backend, mas não o selecionado para os demais plots, mas sim o Matplotlib, adiciona muita complexidade para todos IMHO.

E o custo de movê-los parece pequeno para mim, meu palpite é que nem uma grande proporção de nossos usuários conhece esses lotes. E para aqueles que o fizerem, eles só precisarão instalar um pacote conda extra e usar import pandas_plotting; pandas_plotting.andrews_curves(df) vez de pandas.plotting.andrews_curves(df) .

Para mim parece ganhar muito, a um custo pequeno, mas é claro que é apenas uma opinião.

Podemos documentar que o back-end substituível é apenas para Series / DataFrame.plot? Essa parece uma regra muito simples.

Parece um hack que adiciona complexidade desnecessária para mim; Não acho que explicá-lo na documentação o torne menos contra-intuitivo.

Mas de qualquer maneira, não é grande coisa. Se essa for a opção preferida, é assim que eu a implementaria, pelo menos o aumento na complexidade do código é mínimo: # 27432

Olhando mais de perto agora: se bem entendi, a maneira como o back-end de plotagem será definido está usando:

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

Meu entendimento, então, é que se eu quiser fazer o seguinte trabalho:

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

então, precisarei do pacote altair de nível superior para definir todas as funções em https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py. Eu preferiria não poluir o namespace de nível superior do Altair com todas essas APIs adicionais que não devem ser realmente usadas por usuários do Altair. Na verdade, eu preferiria que a extensão pandas do altair vivesse em um pacote separado, para que não fosse amarrado à cadência de lançamento do próprio Altair.

Se bem entendi, isso significa que não há como fazer pd.set_option('plotting.backend', 'altair') funcionar corretamente sem codificar permanentemente o pacote altair nos pandas da mesma forma que matplotlib está codificado atualmente, correto?

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

Em caso afirmativo, recomendo repensar os meios pelos quais essa API é exposta em pacotes de terceiros.

Minha solução sugerida seria adotar uma estrutura baseada em ponto de entrada que me permitiria, por exemplo, criar um pacote como altair_pandas que registra o ponto de entrada altair para implementar a API. Caso contrário, os usuários ficarão para sempre confusos, pois pd.set_option('plotting.backend', 'altair') não faz o que eles esperam.

Concordou. Acho que os pontos de entrada são o caminho a percorrer. Vou fazer um protótipo de algo.

Na sexta-feira, 19 de julho de 2019 às 13h16 Jake Vanderplas [email protected]
escreveu:

Olhando mais de perto agora: se bem entendi, a forma como
o back-end de plotagem será definido usando:

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

Meu entendimento, então, é que se eu quiser fazer o seguinte trabalho:

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

então vou precisar do pacote altair de nível superior para definir todas as funções
no
https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py.
Eu preferiria não poluir o namespace de nível superior do Altair com todos esses
APIs adicionais. Na verdade, eu preferiria que a extensão dos pandas de altair para
vivem em um pacote separado, por isso não está vinculado à cadência de lançamento de
O próprio Altair.

Se bem entendi, isso significa que não há como fazer pd.set_option ('plotting.backend',
'altair') funcionam corretamente sem codificar o pacote altair nos pandas
a forma como o matplotlib está codificado atualmente, correto?

Nesse caso, recomendo fortemente repensar como isso é ativado por
pacotes de terceiros. Em particular, a adoção de uma estrutura baseada em pontos de entrada
me deixaria criar um pacote como altair_pandas que registra o altair
ponto de entrada. Caso contrário, os usuários ficarão para sempre confusos que pd.set_option ('plotting.backend',
'altair') não faz o que eles esperam.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/pandas-dev/pandas/issues/26747?email_source=notifications&email_token=AAKAOITQM7HH5X4SZ4IAPS3QAIAIBA5CNFSM4HWIMEK2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2ML5OQ#issuecomment-513326778 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AAKAOISFLHDGXLGQ3PUMNLDQAIAIBANCNFSM4HWIMEKQ
.

Houve um momento em que o que você disse estava quase sempre correto, mas esse não é mais o caso.

Se você quiser pandas.options.plotting.backend = 'altair' , em 0,25 você só precisa ter uma função altair.plot() . Em algum momento pensei que seria melhor chamar a função pandas_plot vez de simplesmente plot , então ela era específica em um back-end que tinha outras coisas, mas finalmente não fizemos a alteração.

Se criar a função plot no nível superior de altair for um problema, podemos renomeá-la em uma versão futura ou você também pode ter altair.pandas.plot , mas então os usuários terão que definir pandas.options.plotting.backend = 'altair.pandas' .

Certamente, você mesmo pode alterar a opção assim que os usuários fizerem um import altair . E poderíamos implementar um registro de back-ends. Mas eu acho que seria confuso para os usuários se eles fizessem pandas.options.plotting.backend = 'altair' e ele falhasse, porque eles esqueceram import altair antes.

Uma última coisa é considerar que possivelmente poderíamos ter mais de um backend pandas implementado para altair (ou qualquer outra biblioteca de visualização). Então, para mim, que o nome do back-end não seja altair , não é necessariamente uma coisa ruim.

Aqui está uma implementação baseada em pontos 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
 )

Eu acho que é muito bom. Pacotes de terceiros modificarão seu setup.py (ou pyproject.toml) para incluir algo como

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

Gosto de quebrar o estreito acoplamento entre nomenclatura e implementação.

Não trabalhei com pontos de entrada. Eles são como um registro global do ambiente Python? Sendo novo para eles, não adoro a ideia, mas acho que seria uma maneira razoável de fazer isso então.

Eu ainda gostaria de ter as duas opções, então se o usuário fizer pandas.options.plottting.backend = 'my_own_project.my_custom_small_backend' ele funciona e não requer a criação de um pacote e a definição de pontos de entrada.

Não trabalhei com pontos de entrada. Eles são como um registro global do ambiente Python?

Eu também não usei, mas acho que essa é a ideia. Pelo que entendi, eles são de ferramentas de configuração (mas pacotes como o flit se conectam a eles?). Portanto, eles não fazem parte da biblioteca padrão, mas setuptools é o que todos usam de qualquer maneira.

Eu ainda gostaria de ter as duas opções

Voltar para import_module(backend_name) parece razoável.

Esta página foi útil?
0 / 5 - 0 avaliações