Gráficos apresentáveis no Matplotlib
Publicado em: May 30, 2019 Última modificação: Jul 02, 2020Criar um gráfico de boa qualidade para publicação não é uma tarefa fácil. Existe uma série de fatores que devem ser levados em consideração para que o gráfico resultante consiga transmitir informação de forma clara, precisa e eficiente.
Dentro desses fatores, pode-se citar alguns de natureza estética:
- O tamanho da figura deve se ajustar perfeitamente ao texto do artigo. Por exemplo, para artigos publicados pela American Physical Society (APS) a recomendação é que o tamanho impresso da figura seja de, no máximo, \(\SI{8.5}{\centi\meter}\) para coluna simples;
- O tipo e tamanho das fontes devem ser ajustados para aumentar a legibilidade (de acordo com as recomendações da editora).
- A espessura dos eixos, linhas, formato da legenda também são fatores que influenciam a aparência final do gráfico.
O biblioteca Matplotlib do Python é uma ferramenta incrivelmente flexível para a criação gráficos 2D. Podemos criar um gráfico no Matplotlib com o código
import numpy as np import matplotlib.pyplot as plt def meu_grafico(t): ''' Cria um gráfico de uma série de funções dentro dos valores especificados por um vetor t. ''' fig, ax = plt.subplots(1) ax.plot(t, t**3, label='$x^3$') ax.plot(t, np.sin(2*t), label='$\sin{(2 x)}$') ax.plot(t, np.tanh(5*t), label='$\\tanh{(5 x)}$') ax.set_xlabel('$x$') ax.set_ylabel('$f(x)$') ax.legend() fig.tight_layout() plt.show() x = np.linspace(-1, 1, 100) meu_grafico(x) plt.close('all')
Figura 1: Gráfico do Matplotlib com a configuração padrão.
Esse gráfico não é exatamente "bonito", possuindo um aspecto bastante cru, com uma fonte demasiadamente pequena e as linhas muito finas.
No Matplotlib podemos customizar o gráfico dinamicamente através do objeto rcParams. Por exemplo, podemos aumentar a espessura das linhas do nosso gráfico fazendo
import matplotlib as mpl mpl.rcParams['lines.linewidth'] = 5 # Valor exageradamente alto, apenas # para fins ilustrativos
Com essa configuração teremos:
Figura 2: Gráfico com a largura de linha modificada.
Assim, vamos olhar alguns aspectos da configuração com o rcParams
para deixar o gráfico mais apresentável para publicação. Serão focados três aspectos:
- Tamanho da figura,
- Configuração da fonte,
- Espessura das linhas.
Tamanho da figura
O tamanho da figura é um dos ajustes mais importantes. Um bom ajuste do tamanho da figura garante que você não vai precisar encolhê-la ou aumentá-la quando incluir no texto, garantindo que os tamanhos das fontes serão exatamente os valores definidos.
Vamos otimizar as dimensões da figura para um documento do LaTeX. Precisamos saber a largura (em pontos) do local onde a figura será colocada. Vamos considerar por exemplo a classe article
. Através do documento
\documentclass{article} %% Mude para a classe apropriada \begin{document} \showthe\textwidth % <-- Isto mostra a largura do texto \end{document}
a compilação irá mostrar:
LaTeX2e <2018-12-01> (/usr/share/texmf-dist/tex/latex/base/article.cls Document Class: article 2018/09/03 v1.4i Standard LaTeX document class (/usr/share/texmf-dist/tex/latex/base/size10.clo)) No file teste.aux. > 345.0pt. l.4 \showthe\textwidth % <-- Isto mostra a largura do texto ?
que nos diz que a largura máxima admitida pelo documento é de 345pt. Vamos utilizar o seguinte código para gerar as dimensões da página de uma forma mais atraente:
def figure_size(width, factor = 0.45): ''' width: Número que o LaTeX nos disse factor: Fração da largura da página que se deseja que a figura ocupe ''' fig_width_pt = width * factor inches_per_pt = 1.0/72.27 golden_ratio = (np.sqrt(5) - 1)/2 fig_width_in = fig_width_pt * inches_per_pt # Largura da figura (in) fig_height_in = fig_width_in * golden_ratio # Altura da figura (in) return [fig_width_in, fig_height_in] # Dimensões da figura (in)
Assim, se desejarmos gerar uma figura com que ocupe 95% da página (cuja largura é 345pt), as dimensões "ótimas" da figura serão
[4.535076795350768, 2.8028316011177257]
e a figura resultante será
Figura 3: Gráfico com dimensões otimizadas.
Configuração de fonte
Otimizar o formato das fontes é essencial para que os seus gráficos façam parte do artigo, deixando a leitura mais fluida.
Tamanho da fonte
Como já ajustamos o tamanho da figura, o tamanho da fonte definido aqui será o valor real, já que não há necessidade de encolher ou aumentar a figura. Dessa forma, devemos ajustá-lo de acordo com o documento onde o gráfico será incluído.
Podemos exibir o tamanho da fonte utilizado pelo documento no LaTeX da seguinte forma:
\documentclass{article} \begin{document} \makeatletter \show\f@size \makeatother \end{document}
que irá resultar em
This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2018/Arch Linux) (preloaded format=pdflatex) restricted \write18 enabled. entering extended mode (./teste1.tex LaTeX2e <2018-12-01> (/usr/share/texmf-dist/tex/latex/base/article.cls Document Class: article 2018/09/03 v1.4i Standard LaTeX document class (/usr/share/texmf-dist/tex/latex/base/size10.clo)) (./teste1.aux) > \f@size=macro: ->10. l.4 \show\f@size ?
nesse caso, o tamanho da fonte do documento é 10pt
.
Apesar de ser uma questão de gosto, eu prefiro ajustar o tamanho da fonte dos gráfico um ponto menor do que o do documento.
No Matplotlib existem três elementos textuais principais na figura: os nomes dos eixos; os rótulos dos ticks e o texto das legendas. Nesse caso podemos ajustá-los da seguinte forma:
rcParams['axes.labelsize'] = 9 rcParams['xtick.labelsize'] = 9 rcParams['ytick.labelsize'] = 9 rcParams['legend.fontsize'] = 9
Tipo da fonte
O padrão do matplotlib não é a mesma fonte do LaTeX. Na verdade, ele nem mesmo usa o LaTeX para escrever o texto! Para mudarmos isso, fazemos:
rcParams['font.family'] = 'serif' rcParams['font.serif'] = ['Computer Modern Roman'] rcParams['text.usetex'] = True
Embora no meu caso a maioria dos jornais usem a fonte Computer Modern. Usar uma fonte não padrão requer um pouco mais de trabalho.
Espessura da linha
A espessura de linha padrão do Matplotlib fica razoavelmente boa na versão eletrônica, mas às vezes é quase invisível na versão impressa. Por isso normalmente eu aumento um pouco a espessura da linha, fazendo
rcParams['lines.linewidth'] = 1.5 rcParams['axes.linewidth'] = 1.5 rcParams['xtick.major.width'] = 1.7 # major tick width in points rcParams['xtick.minor.width'] = 1.3 rcParams['ytick.major.width'] = 1.7 # major tick width in points rcParams['ytick.minor.width'] = 1.3
Juntando tudo
Juntando as ideias colocadas acima, temos o seguinte código:
import numpy as np import matplotlib.pyplot as plt from matplotlib import rcParams def figure_size(width, factor = 0.45): ''' width: Número que o LaTeX nos disse factor: Fração da largura da página que se deseja que a figura ocupe ''' fig_width_pt = width * factor inches_per_pt = 1.0/72.27 golden_ratio = (np.sqrt(5) - 1)/2 fig_width_in = fig_width_pt * inches_per_pt # Largura da figura (in) fig_height_in = fig_width_in * golden_ratio # Altura da figura (in) return [fig_width_in, fig_height_in] # Dimensões da figura (in) rcParams['axes.labelsize'] = 9 rcParams['xtick.labelsize'] = 9 rcParams['ytick.labelsize'] = 9 rcParams['legend.fontsize'] = 9 rcParams['font.family'] = 'serif' rcParams['font.serif'] = ['Computer Modern Roman'] rcParams['text.usetex'] = True rcParams['lines.linewidth'] = 1.5 rcParams['axes.linewidth'] = 1.5 rcParams['xtick.major.width'] = 1.7 # major tick width in points rcParams['xtick.minor.width'] = 1.3 rcParams['ytick.major.width'] = 1.7 # major tick width in points rcParams['ytick.minor.width'] = 1.3 def meu_grafico(t): ''' Cria um gráfico de uma série de funções dentro dos valores especificados por um vetor t. ''' fig, ax = plt.subplots(figsize=figure_size(345.0, 0.95)) ax.plot(t, t**3, label='$x^3$') ax.plot(t, np.sin(2*t), label='$\sin{(2 x)}$') ax.plot(t, np.tanh(5*t), label='$\\tanh{(5 x)}$') ax.set_xlabel('$x$') ax.set_ylabel('$f(x)$') ax.legend() fig.tight_layout() plt.show() x = np.linspace(-1, 1, 100) meu_grafico(x) plt.close('all')
Figura 4: Gráfico do Matplotlib customizado
E a figura resultante se encaixará muito melhor no documento!
Estilos
A versão 1.4
do Matplotlib (Agosto de 2014) adicionou um módulo bastante conveniente chamado style
, que inclui um conjunto adicional de estilo, assim como a capacidade de criar os seus próprios. Os estilos são formatados de uma maneira semelhante ao arquivo padrão do Matplotlib, o .matplotlibrc
, devendo ser nomeados com a extensão .mplstyle
.
Os estilos padrão podem ser listados através do comando
import matplotlib.pyplot as plt print(plt.style.available)
que mostra o resultado
['Solarize_Light2', '_classic_test', 'seaborn-talk', 'fast', 'seaborn-dark', 'seaborn-notebook', 'seaborn-pastel', 'seaborn-ticks', 'fivethirtyeight', 'seaborn-poster', 'seaborn-paper', 'seaborn-muted', 'seaborn-white', 'seaborn-bright', 'bmh', 'ggplot', 'seaborn', 'seaborn-deep', 'seaborn-darkgrid', 'seaborn-whitegrid', 'seaborn-dark-palette', 'grayscale', 'dark_background', 'tableau-colorblind10', 'seaborn-colorblind', 'classic']
e um estilo pode ser selecionado fazendo
plt.style.use('ggplot')
assim, podemos gerar o gráfico com a aparência do ggplot:
Figura 5: Gráfico utilizando estilo não-padrão do Matplotlib.
Essa mudança vale para o resto da sessão. Caso deseje fazer apenas um gráfico dentro de um certo estilo, você pode usar uma modificação por contexto:
with plt.style.context('ggplot'): make_a_plot()
Conclusão
Os gráficos padrão do Matplotlib definitivamente não são dos mais belos de se ver, mas a biblioteca disponibiliza uma série de opções para customizar a aparência dos gráficos da forma que o usuário desejar. Mesmo que você não crie o próprio estilo, os que se encontram disponíveis por padrão são extremamente úteis.
Para maiores informações sobre como personalizar a aparência dos gráficos, dê uma olhada na seção do manual do rcParams
.