Interfaces de aplicativo Matplotlib (APIs) #

O Matplotlib tem duas interfaces de aplicativo principais ou estilos de uso da biblioteca:

  • Uma interface "Axes" explícita que usa métodos em um objeto Figure ou Axes para criar outros artistas e construir uma visualização passo a passo. Isso também foi chamado de interface "orientada a objetos".

  • Uma interface "pyplot" implícita que rastreia a última Figura e Eixos criados e adiciona Artistas ao objeto que acha que o usuário deseja.

Além disso, várias bibliotecas downstream (como pandase xarray ) oferecem um plotmétodo implementado diretamente em suas classes de dados para que os usuários possam chamar data.plot().

A diferença entre essas interfaces pode ser um pouco confusa, principalmente devido a trechos na web que usam uma ou outra ou, às vezes, várias interfaces no mesmo exemplo. Aqui, tentamos apontar como as interfaces "pyplot" e downstream se relacionam com a interface "Axes" explícita para ajudar os usuários a navegar melhor na biblioteca.

Interfaces nativas do Matplotlib #

A interface "Axes" explícita #

A interface "Axes" é como o Matplotlib é implementado, e muitas customizações e ajustes finos acabam sendo feitos neste nível.

Essa interface funciona instanciando uma instância de uma Figureclasse ( figabaixo), usando um método de subplotsmétodo (ou similar) nesse objeto para criar um ou mais Axesobjetos ( axabaixo) e, em seguida, chamando métodos de desenho nos eixos ( plotneste exemplo):

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.subplots()
ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

( Código fonte , png )

../../_images/api_interfaces-1.png

Chamamos isso de interface "explícita" porque cada objeto é referenciado explicitamente e usado para criar o próximo objeto. Manter referências aos objetos é muito flexível e nos permite personalizar os objetos depois de criados, mas antes de serem exibidos.

A interface "pyplot" implícita #

O pyplotmódulo sombreia a maioria dos Axesmétodos de plotagem para fornecer o equivalente ao anterior, onde a criação da Figura e dos Eixos é feita pelo usuário:

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2])

( Código fonte , png )

../../_images/api_interfaces-2.png

Isso pode ser conveniente, principalmente ao fazer um trabalho interativo ou scripts simples. Uma referência à figura atual pode ser recuperada usando gcfe aos eixos atuais por gca. O pyplotmódulo retém uma lista de Figuras e cada Figura retém uma lista de Eixos na figura para o usuário, de modo que o seguinte:

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
plt.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
plt.plot([3, 2, 1], [0, 0.5, 0.2])

( Código fonte , png )

../../_images/api_interfaces-3.png

é equivalente a:

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
ax = plt.gca()
ax.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
ax = plt.gca()
ax.plot([3, 2, 1], [0, 0.5, 0.2])

( Código fonte , png )

../../_images/api_interfaces-4.png

Na interface explícita, isso seria:

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2)
axs[0].plot([1, 2, 3], [0, 0.5, 0.2])
axs[1].plot([3, 2, 1], [0, 0.5, 0.2])

( Código fonte , png )

../../_images/api_interfaces-5.png

Por que ser explícito? #

O que acontece se você tiver que retroceder e operar em um eixo antigo que não é referenciado por plt.gca()? Uma maneira simples é chamar subplotnovamente com os mesmos argumentos. No entanto, isso rapidamente se torna deselegante. Você também pode inspecionar o objeto Figure e obter sua lista de objetos Axes, no entanto, isso pode ser enganoso (barras de cores também são Axes!). A melhor solução é provavelmente salvar um identificador para cada Axes que você criar, mas se você fizer isso, por que não simplesmente criar todos os objetos Axes no início?

A primeira abordagem é chamar plt.subplotnovamente:

import matplotlib.pyplot as plt

plt.subplot(1, 2, 1)
plt.plot([1, 2, 3], [0, 0.5, 0.2])

plt.subplot(1, 2, 2)
plt.plot([3, 2, 1], [0, 0.5, 0.2])

plt.suptitle('Implicit Interface: re-call subplot')

for i in range(1, 3):
    plt.subplot(1, 2, i)
    plt.xlabel('Boo')

( Código fonte , png )

../../_images/api_interfaces-6.png

A segunda é salvar um identificador:

import matplotlib.pyplot as plt

axs = []
ax = plt.subplot(1, 2, 1)
axs += [ax]
plt.plot([1, 2, 3], [0, 0.5, 0.2])

ax = plt.subplot(1, 2, 2)
axs += [ax]
plt.plot([3, 2, 1], [0, 0.5, 0.2])

plt.suptitle('Implicit Interface: save handles')

for i in range(2):
    plt.sca(axs[i])
    plt.xlabel('Boo')

( Código fonte , png )

../../_images/api_interfaces-7.png

No entanto, a maneira recomendada seria ser explícito desde o início:

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2)
axs[0].plot([1, 2, 3], [0, 0.5, 0.2])
axs[1].plot([3, 2, 1], [0, 0.5, 0.2])
fig.suptitle('Explicit Interface')
for i in range(2):
    axs[i].set_xlabel('Boo')

( Código fonte , png )

../../_images/api_interfaces-8.png

Interfaces de "objeto de dados" da biblioteca de terceiros #

Algumas bibliotecas de terceiros optaram por implementar plotagem para seus objetos de dados, por exemplo data.plot(), é visto em pandas, xarray e outras bibliotecas de terceiros. Para fins ilustrativos, uma biblioteca downstream pode implementar um contêiner de dados simples que possui dados armazenados juntos e, em seguida, implementa um xmétodo :yplot

import matplotlib.pyplot as plt

# supplied by downstream library:
class DataContainer:

    def __init__(self, x, y):
        """
        Proper docstring here!
        """
        self._x = x
        self._y = y

    def plot(self, ax=None, **kwargs):
        if ax is None:
            ax = plt.gca()
        ax.plot(self._x, self._y, **kwargs)
        ax.set_title('Plotted from DataClass!')
        return ax


# what the user usually calls:
data = DataContainer([0, 1, 2, 3], [0, 0.2, 0.5, 0.3])
data.plot()

( Código fonte , png )

../../_images/api_interfaces-9.png

Assim, a biblioteca pode ocultar todos os detalhes do usuário e pode fazer uma visualização apropriada para o tipo de dados, geralmente com bons rótulos, opções de mapas de cores e outros recursos convenientes.

Acima, no entanto, podemos não ter gostado do título fornecido pela biblioteca. Felizmente, eles nos devolvem os Axes do plot()método e, entendendo a interface Axes explícita, poderíamos chamar: para personalizar o título.ax.set_title('My preferred title')

Muitas bibliotecas também permitem que seus plotmétodos aceitem um argumento ax opcional. Isso nos permite colocar a visualização em um Axes que colocamos e talvez personalizamos.

Resumo #

No geral, é útil entender a interface "Axes" explícita, pois é a mais flexível e subjacente às outras interfaces. Um usuário geralmente pode descobrir como descer para a interface explícita e operar nos objetos subjacentes. Embora a interface explícita possa ser um pouco mais detalhada para configurar, gráficos complicados geralmente acabam mais simples do que tentar usar a interface "pyplot" implícita.

Observação

Às vezes é confuso para as pessoas que importamos pyplotpara ambas as interfaces. Atualmente, o pyplotmódulo implementa a interface "pyplot", mas também fornece métodos de criação de figuras e eixos de nível superior e, por fim, ativa a interface gráfica do usuário, se uma estiver sendo usada. Portanto pyplot, ainda é necessário, independentemente da interface escolhida.

Da mesma forma, as interfaces declarativas fornecidas pelas bibliotecas parceiras usam os objetos acessíveis pela interface "Axes" e geralmente os aceitam como argumentos ou os repassam dos métodos. Geralmente é essencial usar a interface "Axes" explícita para executar qualquer personalização da visualização padrão ou descompactar os dados em arrays NumPy e passar diretamente para o Matplotlib.

Apêndice: Interface "Axes" com estruturas de dados #

A maioria dos Axesmétodos permite ainda outro endereçamento de API passando um objeto de dados para o método e especificando os argumentos como strings:

import matplotlib.pyplot as plt

data = {'xdat': [0, 1, 2, 3], 'ydat': [0, 0.2, 0.4, 0.1]}
fig, ax = plt.subplots(figsize=(2, 2))
ax.plot('xdat', 'ydat', data=data)

( Código fonte , png )

../../_images/api_interfaces-10.png

Apêndice: interface "pylab" #

Há uma outra interface que é altamente desencorajada, que é basicamente fazer . Isso permite que os usuários simplesmente chamem . Embora conveniente, isso pode levar a problemas óbvios se o usuário involuntariamente nomear uma variável com o mesmo nome de um método pyplot.from matplotlib.pyplot import *plot(x, y)