Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Caderno 7

Imersão em matplotlib

Backend x frontend

Backends são mecanismos que agem “por detrás do palco” para construir as figuras. São de dois tipos:

  • Backend interativo: utilizado para produzir figuras para uso em interfaces, tais como PyQt/PySide, PyGObject, Tkinter, wxPython e macOS/Cocoa.

  • Backend de impressão: utilizado para produzir arquivos de imagem em formatos usuais, tais como SVG, PNG, PDF e PS.

Configuração de backend

Útil para modificar propriedades padrão de estilo. Realizada de 3 formas:

  1. Modificando as configurações de execução (runtime configuration), comumente chamadas de rc settings.

  2. Definindo a variável de ambiente MPLBACKEND.

  3. Usando a função matplotlib.use().

Modificando as rc settings

Os parâmetros padrão são definidos em uma variável tipo dict denominada rcParams. Para alterar o backend, devemos alterar a propriedade de mesmo nome no arquivo de configuração matplotlibrc.

  • Localize o arquivo:

Source
import matplotlib as mpl
mpl.matplotlib_fname()
  • Localize a linha do parâmetro backend e a modifique:

## the module name (which must be in the PYTHONPATH) as 'module://my_backend'.
#backend: Agg

Nota: se um backend não for explicitamente definido, o matplotlib usará o disponível no sistema operacional seguindo uma ordem de preferência, assim como se lê no matplotlibrc. Por exemplo:

## The default backend.  If you omit this parameter, the first working
## backend from the following list is used:
##     MacOSX Qt5Agg Gtk3Agg TkAgg WxAgg Agg
## Other choices include:
##     Qt5Cairo GTK3Cairo TkCairo WxCairo Cairo
##     Qt4Agg Qt4Cairo Wx  # deprecated.
##     PS PDF SVG Template
Definindo variável de ambiente

Se este método for usado, uma variável de ambiente pode ser definida para o shell atual ou globalmente. Por exemplo:

export MPLBACKEND=macoxs

Entretanto, defini-la globalmente em arquivos de configuração, tais como .bashrc ou .zshrc pode não ser uma escolha sábia.

Usando a função use()

A função matplotlib.use() deve ser chamada apenas se o script a ser executado depende de um backend específico. Neste caso, a chamada deve preceder a criação de qualquer figura e ser aplicada toda vez que houver alterações de backend entre usuários diferentes. O modo de chamá-la é:

mpl.use('macosx')

Backends predefinidos, renderizadores e canvas

O matplotlib possui backends predefinidos e, em geral, um backend padrão é carregado quando trabalhamos com plotagens, não sendo, a princípio, necessário tomar nenhuma ação por parte do usuário.

Por outro lado, quando interfaces gráficas ou aplicações web são pretendidas, manipular backends pode ser inevitável. Visando facilitar o desenvolvimento de interfaces, o matplotlib segmenta backends em duas classes:

  • renderizadores: entidades responsáveis pelo desenho;

  • canvas: local onde o desenho é feito;

O renderizador canônico para interfaces é o Agg, baseado na biblioteca para C++ Anti-Grain Geometry, responsável pela rasterização das imagens. O Agg é utilizado pelos backends QtAgg, GTK4Agg, GTK3Agg, wxAgg, TkAgg, e macosx. Outro renderizador é baseado na biblioteca Cairo, tal como o QtCairo.

Rasterização e vetorização

Os renderizadores podem ser classificados em dois tipos:

  • rasterizadores: produzem imagens bidimensionais a partir de uma matriz retangular ou grade de pixels. O espaço de cores a que os pixels se submetem determina a imagem. Exemplos de arquivos de imagens rasterizadas são BMP, GIF, JPEG e PNG. São livres de escala.

  • vetorizadores: produzem imagens diretamente a partir de formas geométricas, tais como pontos, retas, curvas e polígonos. Exemplos de arquivos de imagens vetorizadas são AI, CDR, SVG e EPS. A representação do pixel depende da resolução, em geral medida em pontos por polegada, i.e. DPI (dots per inch).

A geração de figuras com os backends de impressão é feita por meio do comando

mpl.pyplot.savefig('arquivo.X')

substituindo X pela extensão do arquivo desejado (ex.: png, pdf, ps, eps, svg, pgf).

Os backends interativos são responsáveis por mostrar as figuras em tela (ex.: qtagg, ipympl, gtk3cairo, etc.)

Dissecando um plot

O matplotlib plota dados em um objeto abstrato chamado “Figura” (Figure), que contém um “eixo” (Axes) ou mais, que define a “área” de plotagem.

A maneira mais simples de se plotar algo é usando pyplot.subplots e Axes.plot como comandos essenciais.

import matplotlib.pyplot as plt

fig, ax = plt.subplots() # cria figura com eixo único
ax.plot(['a','b','c'],[1,-1,0]); # cria objeto lines.Line2D
<Figure size 640x480 with 1 Axes>

Anatomia de uma figura

Em matplotlib, Artist é uma classe abstrata que comporta vários objetos. Figure, Axes, Axis, Text, Line2D, Patch são todos “filhos” de Artist, mas considerados “artistas” do canvas.

O resumo dos objetos é dado como segue:

  • FigureCanvas: é uma espécie de contêiner para a Figura. Grosso modo, seria visto como a tela de pintura onde se passa a tinta.

  • Figure: é um recipiente para um ou mais eixos. É possível criar um número arbitrário de figuras contendo linhas, textos e patches próprios, todos independentes de quaisquer eixos.

  • Axes: é a área retangular que contém os elementos básicos gerados pela plotagem.

  • Axis: são os elementos que fornecem a escala, os limites e as marcas (ticks) e as legendas das marcas (ticklabels) que constituirão os eixos das abcissas e das ordenadas.

Nota: embora Axes signifique “eixo”, este objeto determina a “área” de plotagem. Os objetos Axis (“eixos”) correspondem aos eixos propriamente ditos (2 em 2D; 3 em 3D).

Abaixo, seguiremos um passo a passo para descrever a anatomia de uma figura construída com matplotlib.

  • Figura vazia sem eixos

fig = plt.figure() # figura vazia
#fig.show() # testar em prompt, visto que no Jupyter, o backend é inline.
<Figure size 640x480 with 0 Axes>
  • Figura vazia com 1 eixo

fig, ax = plt.subplots() # figura com 1 eixo
<Figure size 640x480 with 1 Axes>
  • Figura vazia com dois eixos

fig, ax = plt.subplots(1,2) # figura com 2 eixo

<Figure size 640x480 with 2 Axes>
  • Figura com propriedades alteradas

# altera propriedades
mpl.rcParams['figure.facecolor'] = 'blue'
mpl.rcParams['axes.facecolor'] = 'green'
mpl.rcParams['axes.edgecolor'] = 'white'
fig, ax = plt.subplots()
<Figure size 640x480 with 1 Axes>
  • Acessando rc settings.

# localiza parâmetros do objeto especificado
# e imprime o nome de suas propriedades e valores
from re import match

art = 'axes' # axes
for k,v in mpl.rcParams.items():
    if match(art,k):                    
        print((k,v))
('axes.autolimit_mode', 'data')
('axes.axisbelow', 'line')
('axes.edgecolor', 'white')
('axes.facecolor', 'green')
('axes.formatter.limits', [-5, 6])
('axes.formatter.min_exponent', 0)
('axes.formatter.offset_threshold', 4)
('axes.formatter.use_locale', False)
('axes.formatter.use_mathtext', False)
('axes.formatter.useoffset', True)
('axes.grid', False)
('axes.grid.axis', 'both')
('axes.grid.which', 'major')
('axes.labelcolor', 'black')
('axes.labelpad', 4.0)
('axes.labelsize', 'medium')
('axes.labelweight', 'normal')
('axes.linewidth', 0.8)
('axes.prop_cycle', cycler('color', ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']))
('axes.spines.bottom', True)
('axes.spines.left', True)
('axes.spines.right', True)
('axes.spines.top', True)
('axes.titlecolor', 'auto')
('axes.titlelocation', 'center')
('axes.titlepad', 6.0)
('axes.titlesize', 'large')
('axes.titleweight', 'normal')
('axes.titley', None)
('axes.unicode_minus', True)
('axes.xmargin', 0.05)
('axes.ymargin', 0.05)
('axes.zmargin', 0.05)
('axes3d.automargin', False)
('axes3d.grid', True)
('axes3d.mouserotationstyle', 'arcball')
('axes3d.trackballborder', 0.2)
('axes3d.trackballsize', 0.667)
('axes3d.xaxis.panecolor', (0.95, 0.95, 0.95, 0.5))
('axes3d.yaxis.panecolor', (0.9, 0.9, 0.9, 0.5))
('axes3d.zaxis.panecolor', (0.925, 0.925, 0.925, 0.5))
  • Alteração de propriedades selecionadas

mpl.rcParams['axes.spines.left'] = False
mpl.rcParams['axes.spines.right'] = False
fig, ax = plt.subplots()
<Figure size 640x480 with 1 Axes>
  • Alteração de figura com eixos independentes

mpl.rcdefaults() # reset de parâmetros

fig, axs = plt.subplots(1,2,figsize=(4,2),
                        sharey=True)
# print(len(axs)) # note que há 2 eixos
axs[0].set_facecolor('red'); 
axs[0].set_title('A'); 
axs[0].tick_params(axis='x', colors='red')

axs[1].set_facecolor('blue')
axs[1].set_title('B'); 
axs[1].tick_params(axis='x', colors='blue')
<Figure size 400x200 with 2 Axes>
  • Plotagem de curvas

import numpy as np
x = np.linspace(-1,1)
z = np.random.rand(50)

axs[0].plot(x,x,c='w')
axs[0].plot(x,x**2,c='gray')

axs[1].scatter(x,z,c='w')
axs[1].scatter(x,np.sin(z)-1,c='g')

fig
<Figure size 400x200 with 2 Axes>

Template para função de auxílio (helper function) de plotagem

O código abaixo define uma função de auxílio básica para plotagem genérica usando plot.

def h_plot(ax, x, y, params):
    """
    Helper function para gráficos simples e modificação de Line2D.
    """
    p, = ax.plot(x, y, **params)
    return p
np.random.seed(3456)
n = 20
x1,y1,x2,y2 = np.random.rand(n),np.random.rand(n),np.random.rand(n),np.random.rand(n)
fig,(ax1,ax2) = plt.subplots(1,2,
                             sharey=True,
                             figsize=(4,2))

h_plot(ax1,x1,y1,{'ls':'--',
                  'marker':'o',
                  'ms':5,
                  'mfc':'b'})

h_plot(ax2,x2,y2,{'ls':':',
                  'c':'r',
                  'marker':'*',
                  'lw':1,
                  'ms': 10,
                  'mfc':'y'});
<Figure size 400x200 with 2 Axes>

Estilos de codagem

Plotagens genéricas podem ser executadas por meio de dois estilos de codagem:

  • explícito, quando chamamos métodos a partir de objetos Figure e Axes (estilo orientação a objetos);

  • implícito, quando usamos funções do submódulo pyplot para criar esses objetos implicitamente.

Todas as plotagens anteriores foram realizadas pelo modo explícito, mas, vejamos mais um exemplo e como ele se contrasta com o caso implícito.

# exemplo de plotagem EXPLÍCITA (estilo POO)

x = np.arange(2,10) # domínio

fig, ax = plt.subplots(figsize=(4,3))
ax.plot(x,x+1,label='$y = x+1$')
ax.set_xlabel('eixo x')
ax.set_ylabel('eixo y')
ax.legend();
<Figure size 400x300 with 1 Axes>
# exemplo de plotagem IMPLÍCITA (estilo pyplot)

x = np.arange(2,10) # domínio

plt.figure(figsize=(4,3))
plt.plot(x,x+1,label='$y = x+1$')
plt.xlabel('eixo x')
plt.ylabel('eixo y')
plt.legend();
<Figure size 400x300 with 1 Axes>

Nota: o estilo explícito é recomendado para plotagens complicadas ou scripts reutilizáveis, ao passo que o estilo implícito é recomendado para plotagens simples e rápidas.

Estilização do Artist

Para exemplificar estilos, tomaremos como dado de exemplo a série temporal de anomalia de temperatura no período 2020 - 2021 para as coordenadas próximas à cidade de João Pessoa (7.1S,35.0W), disponibilizadas pelo NOAA/NASA.

import pandas as pd

# série temporal de anomalia de temperatura: 2020 - 2021
# (lat,lot) = (7.1S,35.0W) :: JPA
jpa_temp = pd.read_csv('https://www.ncei.noaa.gov/cag/global/time-series/-7.1,-35/land_ocean/12/12/2000-2022/data.csv',
skiprows=3)

ano, temp = jpa_temp['Year'],jpa_temp['Anomaly']

A seguir, temos uma plotagem em mosaico (subplot_mosaic) com formatação independente por eixos. Para criar um mosaico, usamos um formato de matriz com m linhas e n colunas. Cada lista de n elementos deve vir associada à m-ésima linha da matriz como uma “lista de listas”.

O mosaico tem a estrutura de uma matriz 2 x 3. Utilizamos letras para referir-se às linhas, e números às colunas. Neste caso, o quadrante A2 e B3 são dummies.

Primeiramente, plotamos o esquema.

fix, axm = plt.subplot_mosaic([['A1','A1','A3'],['B1','B2','B2']],figsize=(10,7))

axm['A1'].set_title('A1' + ''.center(30,' ') + ' A2 '.center(20,'.'))
axm['A1'].set_xticks([])
axm['A1'].set_yticks([])

axm['A3'].set_xticks([])
axm['A3'].set_yticks([])
axm['A3'].set_title('A3')


axm['B1'].set_title('B1')
axm['B1'].set_xticks([])
axm['B1'].set_yticks([])

axm['B2'].set_xticks([])
axm['B2'].set_yticks([])
axm['B2'].set_title('B2' + ''.center(48,' ') + ' B3 '.center(20,'.'));
<Figure size 1000x700 with 4 Axes>

Em segundo lugar, plotamos os dados reais.

fix, axm = plt.subplot_mosaic([['A1','A1','A3'],['B1','B2','B2']],figsize=(10,7))

axm['A1'].plot(ano,temp,marker='o',c='#ffaabb')
axm['A1'].axhline(y=0,linewidth=0.5,alpha=0.6,color='k')
axm['A1'].set_title('Série 2000 - 2021')

axm['A3'].stem(ano[-5:],temp[-5:],markerfmt='o',linefmt=':',basefmt='-')
axm['A3'].set_title('Série 2017 - 2021')


# setup de cores para linha 2: 
# tons azuis (< 0); tons vermelhos (> 0)
c = ['#e06666']*len(temp)
for i,t in enumerate(temp):
     if t < 0:
        c[i] = '#4ca6ff'
c[temp.idxmax()] = '#f44336'

axm['B1'].bar(ano[:4],temp[:4],color=c)
axm['B1'].set_title('Série 2000 - 2003')

axm['B2'].bar(ano,temp,color=c)
axm['B2'].set_title('Série 2000 - 2021')

axm['B2'].text(2019, -.1, fr'$+{temp.max()}^\circ$',color='#f44336');
<Figure size 1000x700 with 4 Axes>

Estilos padronizados

O matplotlib dispõe de algumas dezenas de estilos padronizados para uso imediato. Para listá-los, invocamos plt.style.available.

for i,e in enumerate(plt.style.available):
    print(i+1,e)
1 Solarize_Light2
2 _classic_test_patch
3 _mpl-gallery
4 _mpl-gallery-nogrid
5 bmh
6 classic
7 dark_background
8 fast
9 fivethirtyeight
10 ggplot
11 grayscale
12 petroff10
13 seaborn-v0_8
14 seaborn-v0_8-bright
15 seaborn-v0_8-colorblind
16 seaborn-v0_8-dark
17 seaborn-v0_8-dark-palette
18 seaborn-v0_8-darkgrid
19 seaborn-v0_8-deep
20 seaborn-v0_8-muted
21 seaborn-v0_8-notebook
22 seaborn-v0_8-paper
23 seaborn-v0_8-pastel
24 seaborn-v0_8-poster
25 seaborn-v0_8-talk
26 seaborn-v0_8-ticks
27 seaborn-v0_8-white
28 seaborn-v0_8-whitegrid
29 tableau-colorblind10

Para carregar os estilos, usamos plt.style.use(<style>), passando como o estilo como argumento.

sty_test = ['classic','ggplot','fivethirtyeight',
'grayscale']

def plot_test(ax,sty):
    exec(f'plt.style.use(\'{sty}\')')
    x = np.linspace(-2,2)
    ax.plot(x,x**2 - 1,label='$y = x^2 - 1$')



for i,s in enumerate(sty_test):
    fig, ax = plt.subplots(figsize=(3,3))
    ax.set_title(sty_test[i])
    ax.set_xticklabels([]); ax.set_yticklabels([])
    plot_test(ax,s)  


# restabelece padrão
plt.rcdefaults(); 
<Figure size 300x300 with 1 Axes>
<Figure size 240x240 with 1 Axes>
<Figure size 240x240 with 1 Axes>
<Figure size 240x240 with 1 Axes>

Estilos personalizados

É possível customizar o estilo das plotagens criando uma folha de estilo (style sheet) personalizada. A style sheet pode ser criada seguindo um padrão similar ao encontrado em CSS.

A folha de estilo possui extensão mplstyle e é carregada com plt.style.use(<arquivo>.mplstyle).

Abaixo temos um extrato de uma style sheet que modifica propriedades de Axes e Grid.

# AXES
axes.titlesize: 16
axes.labelsize: 14

# GRID
axes.grid: True
grid.alpha: 0.5

O código carrega a folha de estilo personalizada deste livro. Depois disso, o estilo das plotagens passa a obedecer às novas regras.

# plotagens em estilo personalizado
plt.style.use('../styles/gcpeixoto-datavis.mplstyle')
for k in range(1,6):
    plt.plot(x,k*x)
<Figure size 640x480 with 1 Axes>
from numpy import sin, pi, linspace, random

np = 100
x = linspace(0,8*pi,np)
y = 0.5*x*sin(x) + random.rand(1,np)

plt.scatter(x,y,s=8)
<Figure size 640x480 with 1 Axes>
plt.rcdefaults()

Introspecção do Artist

A arquitetura do matplotlib possui 3 camadas maiores:

  1. Camada de base (backend layer), a mais interna.

  2. Camada do artista (artista layer), a intermediária.

  3. Camada de código (script layer), a mais externa.

Na maioria do tempo, usuários do matplotlib escrevem códigos “na superfície” e manipulam o estilo visual das plotagens na camada intermediária. Raramente, o trabalho aprofunda-se na camada de base.

A fim de compreendermos melhor a camada Artist, é útil compreender os seguintes elementos:

  • matplotlib.backend_bases.FigureCanvas, a área (tela de pintura) sobre a qual uma figura é desenhada;

  • matplotlib.backend_bases.Rendered, o objeto que cuida da renderização no FigureCanvas (mente de quem pinta);

  • matplotlib.artist.Artist, o objeto que usa o renderizador para pintar (pincéis e tintas);

Os dois primeiros estão na camada de base. O último está na camada intermediária. Como qualquer artista, vamos aprender a trabalhar com os materiais de pintura, propriamente ditos, os pincéis e as tintas.

Adicionalmente, existem dois tipos de matplotlib.artist.Artist:

  • primitivos: representam os objetos gráficos a serem desenhados (Line2D, Rectangle, Text etc.)

  • contêineres: representam os locais onde os objetos serão pintados (Axis, Axes, Figure).

O pipeline de uso do matplotlib basicamente depende da criação de uma Figure e da manipulação de Axes.

Manipulando objetos

# nova figura
fig = plt.figure() 

# eixos
# (x_origem, y_origem, largura, altura)
rect1 = 0.0,1.5,0.5,0.1 
rect2 = 0.1,1.7,0.3,0.2
rect3 = 0.2,2.0,0.1,0.1
ax1 = fig.add_axes(rect1)
ax2 = fig.add_axes(rect2)
ax3 = fig.add_axes(rect3)
<Figure size 640x480 with 3 Axes>

gets e sets

As propriedades dos objetos são acessadas por get e modificadas por set.

# busca todas as propriedades
mpl.artist.getp(ax1)
    adjustable = box
    agg_filter = None
    alpha = None
    anchor = C
    animated = False
    aspect = auto
    autoscale_on = True
    autoscalex_on = True
    autoscaley_on = True
    axes_locator = None
    axisbelow = line
    box_aspect = None
    children = [<matplotlib.spines.Spine object at 0x118430190>, ...
    clip_box = None
    clip_on = True
    clip_path = None
    data_ratio = 1.0
    default_bbox_extra_artists = [<matplotlib.spines.Spine object at 0x118430190>, ...
    facecolor or fc = (1.0, 1.0, 1.0, 1.0)
    figure = Figure(640x480)
    forward_navigation_events = auto
    frame_on = True
    gid = None
    gridspec = None
    images = <a list of 0 AxesImage objects>
    in_layout = True
    label = 
    legend = None
    legend_handles_labels = ([], [])
    lines = <a list of 0 Line2D objects>
    mouseover = False
    navigate = True
    navigate_mode = None
    path_effects = []
    picker = None
    position = Bbox(x0=0.0, y0=1.5, x1=0.5, y1=1.6)
    rasterization_zorder = None
    rasterized = False
    shared_x_axes = <matplotlib.cbook.GrouperView object at 0x11f9f761...
    shared_y_axes = <matplotlib.cbook.GrouperView object at 0x11d562d7...
    sketch_params = None
    snap = None
    subplotspec = None
    tightbbox = Bbox(x0=-18.59722222222222, y0=696.2777777777778, ...
    title = 
    transform = IdentityTransform()
    transformed_clip_path_and_affine = (None, None)
    url = None
    visible = True
    window_extent = TransformedBbox(     Bbox(x0=0.0, y0=1.5, x1=0.5, ...
    xaxis = XAxis(0.0,720.0)
    xaxis_transform = BlendedGenericTransform(     CompositeGenericTrans...
    xbound = (np.float64(0.0), np.float64(1.0))
    xgridlines = <a list of 6 Line2D gridline objects>
    xlabel = 
    xlim = (np.float64(0.0), np.float64(1.0))
    xmajorticklabels = [Text(0.0, 0, '0.0'), Text(0.2, 0, '0.2'), Text(0....
    xmargin = 0.05
    xminorticklabels = []
    xscale = linear
    xticklabels = [Text(0.0, 0, '0.0'), Text(0.2, 0, '0.2'), Text(0....
    xticklines = <a list of 12 Line2D ticklines objects>
    xticks = [0.  0.2 0.4 0.6 0.8 1. ]
    yaxis = YAxis(0.0,720.0)
    yaxis_transform = BlendedGenericTransform(     BboxTransformTo(     ...
    ybound = (np.float64(0.0), np.float64(1.0))
    ygridlines = <a list of 2 Line2D gridline objects>
    ylabel = 
    ylim = (np.float64(0.0), np.float64(1.0))
    ymajorticklabels = [Text(0, 0.0, '0'), Text(0, 1.0, '1')]
    ymargin = 0.05
    yminorticklabels = []
    yscale = linear
    yticklabels = [Text(0, 0.0, '0'), Text(0, 1.0, '1')]
    yticklines = <a list of 4 Line2D ticklines objects>
    yticks = [0. 1.]
    zorder = 0
# busca valor da propriedade `get_frame_on()`
ax1.get_frame_on()
fig
<Figure size 640x480 with 3 Axes>
# altera valor da propriedade `get_frame_on()`
ax1.set_frame_on(False)
fig
<Figure size 640x480 with 3 Axes>

Estilização temporária

# Retorna uma lista com todos os nomes válidos
print(*plt.style.available, sep="\n")
Solarize_Light2
_classic_test_patch
_mpl-gallery
_mpl-gallery-nogrid
bmh
classic
dark_background
fast
fivethirtyeight
ggplot
grayscale
petroff10
seaborn-v0_8
seaborn-v0_8-bright
seaborn-v0_8-colorblind
seaborn-v0_8-dark
seaborn-v0_8-dark-palette
seaborn-v0_8-darkgrid
seaborn-v0_8-deep
seaborn-v0_8-muted
seaborn-v0_8-notebook
seaborn-v0_8-paper
seaborn-v0_8-pastel
seaborn-v0_8-poster
seaborn-v0_8-talk
seaborn-v0_8-ticks
seaborn-v0_8-white
seaborn-v0_8-whitegrid
tableau-colorblind10
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0.5,3)

with plt.style.context('bmh'):
    plt.plot(x,np.log(x),c='#117029');
<Figure size 640x480 with 1 Axes>
with plt.style.context('seaborn-v0_8'):
    plt.plot(x,np.log(x),c='#117029');
<Figure size 800x550 with 1 Axes>