3º Avaliação: plotagem e aceleração computacional em Python
Autor
Wericky Barbosa de Melo
introdução
Este trabalho tem como objetivo explorar importantes ferramentas do ecossistema python voltadas para plotagem, em outras palavras, uma representação mais visualização dos dados.
Além disso será apresentado conceitos acerca de aceleração computacional, utilizando o Numba juntamente a um exemplo comparativo.
Dica
Para visualizar os códigos, basta clicar nos botão |>Código
Parte 1: plotagem em python
Plotagem em python é do criar uma representação visual dos dados, como por exemplo gráficos e tabelas, para isto podemos utilizar alguns pacotes como por exemplo Matplotlib, Seaborn, Plotly e Plotnine.
matplotlib
Esta é uma biblioteca de baixo nível, altamente flexível e configurável, que serve de base para outras bibliotecas como o Seaborn por exemplo.
Exemplo de plotagem de gráficos utilizando matplotlib
gráfico de linha
Código
import matplotlib.pyplot as pltimport numpy as npN =21x = np.linspace(0, 10, 11)y = [3.9, 4.4, 10.8, 10.3, 11.2, 13.1, 14.1, 9.9, 13.9, 15.1, 12.5]# fit a linear curve and estimate its y-values and their error.a, b = np.polyfit(x, y, deg=1)y_est = a * x + by_err = x.std() * np.sqrt(1/len(x) + (x - x.mean())**2/ np.sum((x - x.mean())**2))fig, ax = plt.subplots()ax.plot(x, y_est, '-')ax.fill_between(x, y_est - y_err, y_est + y_err, alpha=0.2)ax.set_title('Gráfico de linha')ax.plot(x, y, 'o', color='tab:brown')
Dica
Para visualizar os códigos, basta clicar nos botão |> Código
O Seaborn utiliza o matplotlib para funcionar e simplifica o uso, a maioria de suas funções permitem que lide diretamente com dataframes e criam gráficos com poucas linhas.
gráfico de dispersão
Código
import seaborn as snsimport matplotlib.pyplot as pltsns.set_theme(style="whitegrid")# Load the example diamonds datasetdiamonds = sns.load_dataset("diamonds")# Draw a scatter plot while assigning point colors and sizes to different# variables in the datasetf, ax = plt.subplots(figsize=(6.5, 6.5))sns.despine(f, left=True, bottom=True)clarity_ranking = ["I1", "SI2", "SI1", "VS2", "VS1", "VVS2", "VVS1", "IF"]sns.scatterplot(x="carat", y="price", hue="clarity", size="depth", palette="ch:r=-.2,d=.3_r", hue_order=clarity_ranking, sizes=(1, 8), linewidth=0, data=diamonds, ax=ax)
Dica
Para visualizar os códigos, basta clicar nos botão |> Código
import seaborn as snssns.set_theme(style="ticks", palette="pastel")# Load the example tips datasettips = sns.load_dataset("tips")# Draw a nested boxplot to show bills by day and timesns.boxplot(x="day", y="total_bill", hue="smoker", palette=["m", "g"], data=tips)sns.despine(offset=10, trim=True)
Possui um foco maior na interatividade. Como por exemplo para a construção de dashboards e gráficos interativos, permitindo zoom ou seleção por exemplo.
Aviso
Alguns gráficos podem demorar para renderizar dependendo do tamanho dos dados.
Numba é um pacote que acelera a execução de funções númericas utilizando a compilação JIT (Just-In-Time). Isto é, traduzindo funções python em um código de máquina otimizado, aumentando o desempenho e diminuindo o tempo de processamento. Esta otimizização é especialmente útil para realizar simulação de dados em larga escala.
Vamos seguir com um exemplo de código não-otimizado que demanda um tempo considerável para funcionar:
Código sem otimização
Código
# 1) Imports e sementeimport timeimport randomimport matplotlib.pyplot as pltfrom matplotlib.patches import Arcrandom.seed(123)# 2) Função de aproximação de π (Python puro)def aproximar_pi_calculo_py(num_pontos): dentro =0for _ inrange(num_pontos): x = random.uniform(0, 1) y = random.uniform(0, 1)if x*x + y*y <=1: dentro +=1return4* dentro / num_pontos# 3) Função de visualização (Monte Carlo)def visualizar_aproximacao(num_pontos_plot): dentro_x, dentro_y = [], [] fora_x, fora_y = [], []for _ inrange(num_pontos_plot): x, y = random.uniform(0, 1), random.uniform(0, 1)if x*x + y*y <=1: dentro_x.append(x) dentro_y.append(y)else: fora_x.append(x) fora_y.append(y) pi_grafico =4*len(dentro_x) / num_pontos_plot fig, ax = plt.subplots(figsize=(6,6)) ax.scatter(dentro_x, dentro_y, color='blue', s=1, label='Dentro do círculo') ax.scatter(fora_x, fora_y, color='red', s=1, label='Fora do círculo') circ = Arc((0, 0), 2, 2, theta1=0, theta2=90, color='green', linewidth=2) ax.add_patch(circ) ax.set_aspect('equal') ax.set_xlim(0,1) ax.set_ylim(0,1) ax.legend(loc="lower right") plt.title(f'Aproximação de π (Monte Carlo)\nπ ≈ {pi_grafico:.5f} com {num_pontos_plot} pontos') plt.show()# 4) Execução e medição de temposif__name__=="__main__": N =100_000# chamando função t0 = time.time() pi1 = aproximar_pi_calculo_py(N) t1 = time.time()print(f"[Python puro] π ≈ {pi1:.5f} tempo: {t1-t0:.4f} segundos")# Visualização dos pontos visualizar_aproximacao(N)
[Python puro] π ≈ 3.13860 tempo: 0.0554 segundos
Agora otimizado com Numba:
Código
import timeimport randomimport numpy as npimport matplotlib.pyplot as pltfrom matplotlib.patches import Arcfrom numba import njit, prange# 1) Função original em Python purodef aproximar_pi_calculo_py(num_pontos): dentro =0for _ inrange(num_pontos): x = random.uniform(0, 1) y = random.uniform(0, 1)if x*x + y*y <=1: dentro +=1return4* dentro / num_pontos# 2) Função otimizada com Numba (parallel loop + PRNG do NumPy)@njit(parallel=True)def aproximar_pi_calculo_numba(num_pontos): dentro =0for i in prange(num_pontos): x = np.random.random() y = np.random.random()if x*x + y*y <=1: dentro +=1return4* dentro / num_pontos# 3) Visualização (igual ao seu código original)def visualizar_aproximacao(num_pontos_plot): dentro_x, dentro_y, fora_x, fora_y = [], [], [], []for _ inrange(num_pontos_plot): x, y = random.uniform(0,1), random.uniform(0,1)if x*x + y*y <=1: dentro_x.append(x); dentro_y.append(y)else: fora_x.append(x); fora_y.append(y) pi_grafico =4*len(dentro_x) / num_pontos_plot fig, ax = plt.subplots(figsize=(6,6)) ax.scatter(dentro_x, dentro_y, color='blue', s=1, label='Dentro do círculo') ax.scatter(fora_x, fora_y, color='red', s=1, label='Fora do círculo') circ = Arc((0,0), 2, 2, theta1=0, theta2=90, color='green', linewidth=2) ax.add_patch(circ) ax.set_aspect('equal') ax.set_xlim(0,1); ax.set_ylim(0,1) ax.legend(loc="lower right") plt.title(f'Aproximação de π (Monte Carlo)\nπ ≈ {pi_grafico:.5f} com {num_pontos_plot} pontos') plt.show()# 4) Semente para Python + NumPy (garante reproducibilidade em NumPy)random.seed(123)np.random.seed(123)# 5) Medindo temposN =100_000# 5.1 Python purot0 = time.time()pi_py = aproximar_pi_calculo_py(N)t1 = time.time()print(f"[Python] π ≈ {pi_py:.5f} tempo: {t1-t0:.4f} segundos")# 5.2 Numba: primeira chamada (compilação + execução)t2 = time.time()pi_nb_first = aproximar_pi_calculo_numba(N)t3 = time.time()print(f"[Numba] compilação+execução π ≈ {pi_nb_first:.5f} tempo: {t3-t2:.4f} s")# 5.3 Numba: segunda chamada (só execução)t4 = time.time()pi_nb = aproximar_pi_calculo_numba(N)t5 = time.time()print(f"[Numba] apenas execução π ≈ {pi_nb:.5f} tempo: {t5-t4:.4f} s")# 6) Visualizarvisualizar_aproximacao(N)
[Python] π ≈ 3.13860 tempo: 0.0553 segundos
[Numba] compilação+execução π ≈ 3.15048 tempo: 1.1240 s
[Numba] apenas execução π ≈ 3.14440 tempo: 0.0006 s
Dica
Note que o tempo da versão otimizada do código é mais rápida, necessitando menos tempo para executar os cálculos.