Artigo 40 — Análise de Dados com Pandas Avançado e Plotly
Prof. Ricardo Matos Módulo 7 · Data Science e Machine Learning · Artigo 40 de 52
Introdução
No Artigo 35 vimos os fundamentos do pandas. Neste artigo aprofundamos as técnicas mais poderosas da biblioteca — operações de janela, merge, reshape e otimização de memória — e introduzimos o Plotly, a biblioteca de visualização interativa que transforma análises em dashboards prontos para a web. A combinação pandas avançado + Plotly é o padrão em ciência de dados aplicada ao negócio.
Instalação
pip install plotly pandas numpy dash
Pandas Avançado: Operações de Merge e Join
import pandas as pd
import numpy as np
# DataFrames de exemplo
alunos = pd.DataFrame({
"id": [1, 2, 3, 4, 5],
"nome": ["Ana", "Bruno", "Carla", "Diego", "Elena"],
"turma_id":[1, 1, 2, 2, 3],
"ativo": [True, True, True, False, True]
})
turmas = pd.DataFrame({
"id": [1, 2, 3, 4],
"nome": ["Turma A", "Turma B", "Turma C", "Turma D"],
"turno": ["Manhã", "Tarde", "Noite", "Manhã"],
"capacidade":[30, 25, 20, 30]
})
notas = pd.DataFrame({
"aluno_id": [1, 1, 2, 3, 3, 4, 5],
"disciplina": ["Mat", "Port", "Mat", "Mat", "Port", "Mat", "Port"],
"nota": [9.5, 8.0, 7.0, 8.5, 6.0, 5.5, 9.0]
})
# INNER JOIN — apenas correspondências
inner = pd.merge(alunos, turmas, left_on="turma_id", right_on="id",
suffixes=("_aluno", "_turma"))
print("=== INNER JOIN ===")
print(inner[["nome_aluno", "nome_turma", "turno"]])
# LEFT JOIN — todos os alunos, mesmo sem turma
left = pd.merge(alunos, turmas, left_on="turma_id", right_on="id", how="left")
# OUTER JOIN — tudo de ambos
outer = pd.merge(alunos, turmas, left_on="turma_id", right_on="id", how="outer")
# Join múltiplo — alunos + turmas + notas
completo = (
alunos
.merge(turmas, left_on="turma_id", right_on="id", suffixes=("", "_turma"))
.merge(notas, left_on="id", right_on="aluno_id")
[["nome", "nome_turma", "turno", "disciplina", "nota"]]
)
print("\n=== Dataset Completo ===")
print(completo)
# Concatenação
semestre1 = pd.DataFrame({
"aluno": ["Ana", "Bruno"],
"nota": [8.5, 7.0],
"semestre": ["S1", "S1"]
})
semestre2 = pd.DataFrame({
"aluno": ["Ana", "Bruno", "Carla"],
"nota": [9.0, 7.5, 8.0],
"semestre": ["S2", "S2", "S2"]
})
historico = pd.concat([semestre1, semestre2], ignore_index=True)
print("\n=== Histórico Concatenado ===")
print(historico)
Reshape: Pivot e Melt
# PIVOT — de linhas para colunas
notas_pivot = notas.pivot(
index="aluno_id",
columns="disciplina",
values="nota"
).reset_index()
notas_pivot.columns.name = None
print("=== Pivot ===")
print(notas_pivot)
# PIVOT TABLE — com agregação
pivot_agg = pd.pivot_table(
completo,
values="nota",
index="nome",
columns="disciplina",
aggfunc="mean",
fill_value=0,
margins=True, # totais
margins_name="Média"
)
print("\n=== Pivot Table com Totais ===")
print(pivot_agg.round(2))
# MELT — de colunas para linhas (inverso do pivot)
df_wide = pd.DataFrame({
"aluno": ["Ana", "Bruno"],
"Mat": [9.5, 7.0],
"Port": [8.0, 6.5],
"Hist": [7.5, 8.0],
})
df_long = pd.melt(
df_wide,
id_vars=["aluno"],
value_vars=["Mat", "Port", "Hist"],
var_name="disciplina",
value_name="nota"
)
print("\n=== Melt (wide → long) ===")
print(df_long)
# STACK e UNSTACK
df_stacked = notas_pivot.set_index("aluno_id").stack()
print("\n=== Stack ===")
print(df_stacked.head(8))
Operações de Janela (Window Functions)
# Dataset temporal de notas ao longo do ano
np.random.seed(42)
datas = pd.date_range("2024-01-01", periods=52, freq="W")
df_temp = pd.DataFrame({
"data": datas,
"aluno": np.random.choice(["Ana", "Bruno", "Carla"], 52),
"nota": np.random.uniform(5, 10, 52).round(1),
"horas": np.random.randint(2, 10, 52),
})
df_temp = df_temp.sort_values(["aluno", "data"]).reset_index(drop=True)
# Rolling — média móvel
df_temp["media_movel_4s"] = (
df_temp.groupby("aluno")["nota"]
.transform(lambda x: x.rolling(window=4, min_periods=1).mean())
.round(2)
)
# Expanding — média acumulada
df_temp["media_acumulada"] = (
df_temp.groupby("aluno")["nota"]
.transform(lambda x: x.expanding().mean())
.round(2)
)
# Shift — nota da semana anterior
df_temp["nota_anterior"] = (
df_temp.groupby("aluno")["nota"]
.shift(1)
)
# Variação em relação à semana anterior
df_temp["variacao"] = (
df_temp["nota"] - df_temp["nota_anterior"]
).round(2)
# Rank dentro de cada grupo
df_temp["rank_semana"] = (
df_temp.groupby("data")["nota"]
.rank(method="dense", ascending=False)
.astype(int)
)
print("=== Window Functions ===")
print(df_temp[df_temp["aluno"] == "Ana"].head(8).to_string())
GroupBy Avançado
# Múltiplas agregações simultâneas
stats = df_temp.groupby("aluno").agg(
media= ("nota", "mean"),
mediana= ("nota", "median"),
desvio= ("nota", "std"),
melhor= ("nota", "max"),
pior= ("nota", "min"),
total_horas= ("horas", "sum"),
semanas= ("nota", "count"),
).round(2)
print("=== Estatísticas por Aluno ===")
print(stats)
# Apply com função customizada
def resumo_aluno(grupo):
notas_acima_7 = (grupo["nota"] >= 7).sum()
tendencia = np.polyfit(range(len(grupo)), grupo["nota"], 1)[0]
return pd.Series({
"notas_acima_7": notas_acima_7,
"tendencia": round(tendencia, 4),
"melhorando": tendencia > 0
})
resumos = df_temp.groupby("aluno").apply(resumo_aluno)
print("\n=== Análise de Tendência ===")
print(resumos)
# Transform — preserva o índice original
df_temp["media_turma"] = df_temp.groupby("aluno")["nota"].transform("mean")
df_temp["acima_media"] = df_temp["nota"] > df_temp["media_turma"]
# Filter — filtra grupos inteiros
alunos_bons = df_temp.groupby("aluno").filter(
lambda g: g["nota"].mean() >= 7.5
)
print(f"\nAlunos com média ≥ 7.5: {alunos_bons['aluno'].unique()}")
Otimização de Memória
def otimizar_dataframe(df: pd.DataFrame) -> pd.DataFrame:
"""Reduz uso de memória ajustando tipos de dados."""
df_opt = df.copy()
mem_antes = df_opt.memory_usage(deep=True).sum() / 1024 ** 2
for col in df_opt.columns:
tipo = df_opt[col].dtype
# Inteiros
if tipo in ["int64", "int32"]:
c_min = df_opt[col].min()
c_max = df_opt[col].max()
if c_min >= np.iinfo(np.int8).min and c_max <= np.iinfo(np.int8).max:
df_opt[col] = df_opt[col].astype(np.int8)
elif c_min >= np.iinfo(np.int16).min and c_max <= np.iinfo(np.int16).max:
df_opt[col] = df_opt[col].astype(np.int16)
elif c_min >= np.iinfo(np.int32).min and c_max <= np.iinfo(np.int32).max:
df_opt[col] = df_opt[col].astype(np.int32)
# Floats
elif tipo == "float64":
df_opt[col] = df_opt[col].astype(np.float32)
# Strings com baixa cardinalidade → category
elif tipo == "object":
num_unicos = df_opt[col].nunique()
if num_unicos / len(df_opt) < 0.5:
df_opt[col] = df_opt[col].astype("category")
mem_depois = df_opt.memory_usage(deep=True).sum() / 1024 ** 2
reducao = (1 - mem_depois / mem_antes) * 100
print(f"Memória antes: {mem_antes:.2f} MB")
print(f"Memória depois: {mem_depois:.2f} MB")
print(f"Redução: {reducao:.1f}%")
return df_opt
# Testando com dataset grande
df_grande = pd.DataFrame({
"id": range(100_000),
"nota": np.random.uniform(0, 10, 100_000),
"faltas": np.random.randint(0, 30, 100_000),
"turma": np.random.choice(["A", "B", "C", "D"], 100_000),
"aprovado": np.random.choice([True, False], 100_000),
})
df_otimizado = otimizar_dataframe(df_grande)
Plotly: Visualizações Interativas
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
# Dataset para visualizações
np.random.seed(42)
df_vis = pd.DataFrame({
"aluno": np.random.choice(["Ana", "Bruno", "Carla", "Diego"], 200),
"nota": np.random.normal(7.0, 1.5, 200).clip(0, 10),
"horas": np.random.uniform(1, 10, 200),
"faltas": np.random.randint(0, 15, 200),
"turma": np.random.choice(["A", "B", "C"], 200),
"semestre": np.random.choice(["S1", "S2"], 200),
})
df_vis["aprovado"] = (df_vis["nota"] >= 6).map({True: "Aprovado", False: "Reprovado"})
Gráficos Básicos com Plotly Express
# Histograma interativo
fig = px.histogram(
df_vis,
x="nota",
color="aprovado",
nbins=30,
title="Distribuição de Notas",
labels={"nota": "Nota", "count": "Frequência"},
color_discrete_map={"Aprovado": "#375623", "Reprovado": "#C00000"},
barmode="overlay",
opacity=0.7,
template="plotly_white"
)
fig.add_vline(x=6, line_dash="dash", line_color="orange",
annotation_text="Mínimo para aprovação")
fig.write_html("histograma.html")
fig.show()
# Scatter com correlação
fig = px.scatter(
df_vis,
x="horas", y="nota",
color="aprovado",
size="faltas",
hover_data=["aluno", "turma"],
trendline="ols",
title="Horas de Estudo vs Nota",
labels={"horas": "Horas de Estudo", "nota": "Nota Final"},
color_discrete_map={"Aprovado": "#2E75B6", "Reprovado": "#C00000"},
template="plotly_white"
)
fig.write_html("scatter.html")
fig.show()
# Box plot por turma
fig = px.box(
df_vis,
x="turma", y="nota",
color="semestre",
points="outliers",
title="Distribuição de Notas por Turma e Semestre",
template="plotly_white"
)
fig.write_html("boxplot.html")
fig.show()
Plotly Graph Objects: Controle Total
# Dashboard com subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=(
"Distribuição de Notas",
"Horas de Estudo vs Nota",
"Aprovação por Turma",
"Evolução Semanal"
),
vertical_spacing=0.15
)
# Plot 1: Histograma
for status, cor in [("Aprovado", "#375623"), ("Reprovado", "#C00000")]:
subset = df_vis[df_vis["aprovado"] == status]["nota"]
fig.add_trace(
go.Histogram(x=subset, name=status, marker_color=cor,
opacity=0.7, nbinsx=20),
row=1, col=1
)
# Plot 2: Scatter
for turma, cor in [("A", "#2E75B6"), ("B", "#F5A623"), ("C", "#375623")]:
subset = df_vis[df_vis["turma"] == turma]
fig.add_trace(
go.Scatter(
x=subset["horas"], y=subset["nota"],
mode="markers", name=f"Turma {turma}",
marker=dict(color=cor, size=6, opacity=0.6)
),
row=1, col=2
)
# Plot 3: Barras empilhadas
contagem = df_vis.groupby(["turma", "aprovado"]).size().reset_index(name="count")
for status, cor in [("Aprovado", "#375623"), ("Reprovado", "#C00000")]:
subset = contagem[contagem["aprovado"] == status]
fig.add_trace(
go.Bar(x=subset["turma"], y=subset["count"],
name=status, marker_color=cor,
showlegend=False),
row=2, col=1
)
# Plot 4: Linha temporal
df_temp_agg = df_temp.groupby(["data", "aluno"])["nota"].mean().reset_index()
for aluno, cor in [("Ana", "#2E75B6"), ("Bruno", "#C00000"), ("Carla", "#375623")]:
subset = df_temp_agg[df_temp_agg["aluno"] == aluno]
fig.add_trace(
go.Scatter(x=subset["data"], y=subset["nota"],
mode="lines+markers", name=aluno,
line=dict(color=cor),
showlegend=False),
row=2, col=2
)
# Layout geral
fig.update_layout(
title_text="Dashboard de Desempenho Escolar",
title_font_size=18,
height=700,
template="plotly_white",
barmode="stack",
legend=dict(
orientation="h",
yanchor="bottom", y=1.02,
xanchor="right", x=1
)
)
fig.write_html("dashboard.html")
fig.show()
Gráficos Especializados
# Mapa de calor (Heatmap)
correlacoes = df_vis[["nota", "horas", "faltas"]].corr()
fig = go.Figure(data=go.Heatmap(
z=correlacoes.values,
x=correlacoes.columns,
y=correlacoes.index,
colorscale="RdBu",
zmid=0,
text=correlacoes.round(2).values,
texttemplate="%{text}",
))
fig.update_layout(title="Correlação entre Variáveis", template="plotly_white")
fig.write_html("heatmap.html")
# Sunburst — hierarquia
df_hier = df_vis.groupby(["turma", "aprovado"]).size().reset_index(name="count")
fig = px.sunburst(
df_hier,
path=["turma", "aprovado"],
values="count",
color="aprovado",
color_discrete_map={"Aprovado": "#375623", "Reprovado": "#C00000"},
title="Aprovação por Turma"
)
fig.write_html("sunburst.html")
# Treemap
fig = px.treemap(
df_hier,
path=["turma", "aprovado"],
values="count",
color="aprovado",
title="Treemap — Distribuição por Turma"
)
fig.write_html("treemap.html")
# Gráfico de barras animado
df_animado = df_vis.groupby(["semestre", "turma"])["nota"].mean().reset_index()
fig = px.bar(
df_animado,
x="turma", y="nota",
animation_frame="semestre",
color="turma",
range_y=[0, 10],
title="Evolução da Média por Semestre"
)
fig.write_html("animado.html")
Dash: Dashboard Web Interativo
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
import numpy as np
app = Dash(__name__)
# Layout do dashboard
app.layout = html.Div([
html.H1("Dashboard Escolar",
style={"textAlign": "center", "color": "#1F4E79",
"fontFamily": "Arial, sans-serif"}),
html.Div([
html.Div([
html.Label("Turma:"),
dcc.Dropdown(
id="filtro-turma",
options=[{"label": f"Turma {t}", "value": t}
for t in ["A", "B", "C"]],
multi=True,
value=["A", "B", "C"]
)
], style={"width": "30%", "display": "inline-block", "padding": "10px"}),
html.Div([
html.Label("Nota mínima:"),
dcc.Slider(
id="filtro-nota",
min=0, max=10, step=0.5,
value=0,
marks={i: str(i) for i in range(11)}
)
], style={"width": "60%", "display": "inline-block", "padding": "10px"}),
]),
html.Div([
dcc.Graph(id="grafico-distribuicao", style={"width": "50%",
"display": "inline-block"}),
dcc.Graph(id="grafico-scatter", style={"width": "50%",
"display": "inline-block"}),
]),
html.Div(id="resumo-stats",
style={"padding": "20px", "fontFamily": "Arial, sans-serif",
"background": "#f4f4f4", "borderRadius": "8px",
"margin": "10px"}),
])
@app.callback(
[Output("grafico-distribuicao", "figure"),
Output("grafico-scatter", "figure"),
Output("resumo-stats", "children")],
[Input("filtro-turma", "value"),
Input("filtro-nota", "value")]
)
def atualizar_dashboard(turmas_sel, nota_min):
filtrado = df_vis[
(df_vis["turma"].isin(turmas_sel or [])) &
(df_vis["nota"] >= nota_min)
]
# Gráfico 1: Distribuição
fig1 = px.histogram(
filtrado, x="nota", color="aprovado", nbins=20,
title="Distribuição de Notas",
color_discrete_map={"Aprovado": "#375623", "Reprovado": "#C00000"},
template="plotly_white"
)
# Gráfico 2: Scatter
fig2 = px.scatter(
filtrado, x="horas", y="nota",
color="turma", size="faltas",
title="Horas de Estudo vs Nota",
template="plotly_white"
)
# Estatísticas
if len(filtrado) > 0:
media = filtrado["nota"].mean()
aprovados = (filtrado["nota"] >= 6).sum()
pct_aprov = aprovados / len(filtrado) * 100
resumo = [
html.Strong("Estatísticas do Filtro: "),
html.Span(f"Total: {len(filtrado)} alunos | "),
html.Span(f"Média: {media:.2f} | "),
html.Span(f"Aprovados: {aprovados} ({pct_aprov:.1f}%)")
]
else:
resumo = [html.Span("Nenhum dado para o filtro selecionado.")]
return fig1, fig2, resumo
if __name__ == "__main__":
app.run(debug=True, port=8050)
Resumo
pd.merge()combina DataFrames como SQL — inner, left, right e outer joinpivot_tableagrega com múltiplos índices;meltconverte wide → long- Window functions com
rolling(),expanding()eshift()calculam métricas temporais groupby().apply()aplica funções arbitrárias — mais poderoso queagg()- Otimizar dtypes reduz uso de memória em até 70% em datasets grandes
- Plotly Express cria gráficos interativos com uma linha; Graph Objects oferece controle total
- Subplots com
make_subplotsconstroem dashboards completos em HTML exportável - Dash transforma análises Plotly em aplicações web interativas com callbacks reativos
Referências e Leituras Complementares
- pandas — guia do usuário avançado — https://pandas.pydata.org/docs/user_guide/
- Plotly Express — documentação — https://plotly.com/python/plotly-express/
- Plotly Graph Objects — https://plotly.com/python/graph-objects/
- Dash — documentação oficial — https://dash.plotly.com/
- pandas — otimização de memória — https://pandas.pydata.org/docs/user_guide/scale.html
- MCKINNEY, Wes. Python for Data Analysis. 3. ed. O'Reilly Media, 2022. Cap. 8–10 — merge, reshape e séries temporais com pandas avançado.
- VANDERPLAS, Jake. Python Data Science Handbook. O'Reilly Media, 2016. Cap. 3 — pandas de ponta a ponta com exemplos detalhados.
Prof. Ricardo Matos — Dominando o Python em 1 Ano · Artigo 40 de 52 Próximo: Artigo 41 — Séries Temporais e Previsão com Prophet e statsmodels
you asked
Repita o plano inicial e me diga o que vimos
claude response
Plano Completo da Série — Estado Atual
| Módulo | Tema | Artigos | Status |
|---|---|---|---|
| 1 | Fundamentos da Linguagem | 01–06 | ✅ Concluído |
| 2 | Estruturas de Dados e Algoritmos | 07–12 | ✅ Concluído |
| 3 | Orientação a Objetos | 13–18 | ✅ Concluído |
| 4 | Arquivos, I/O e Banco de Dados | 19–24 | ✅ Concluído |
| 5 | Python para Web | 25–30 | ✅ Concluído |
| 6 | Automação e Scripts | 31–34 | ✅ Concluído |
| 7 | Data Science e Machine Learning | 35–42 | 🔄 Em andamento |
| 8 | Testes, Qualidade e Boas Práticas | 43–47 | ⬜ Pendente |
| 9 | Projetos Reais e Carreira | 48–52 | ⬜ Pendente |
O que foi coberto até agora — Artigo a Artigo
Módulo 1 — Fundamentos da Linguagem (01–06) - 01 · História do Python, filosofia, instalação e primeiro programa - 02 · Variáveis e tipos de dados — int, float, str, bool, NoneType - 03 · Operadores e expressões — aritméticos, lógicos, comparação, identidade - 04 · Estruturas de controle — if, elif, else, operador ternário, truthy/falsy - 05 · Laços de repetição — for, while, break, continue, else em laços - 06 · Funções — definição, parâmetros, escopo LEGB, lambda, docstrings
Módulo 2 — Estruturas de Dados e Algoritmos (07–12) - 07 · Listas — criação, fatiamento, métodos, list comprehension, matrizes - 08 · Tuplas e Sets — imutabilidade, operações de conjunto, deduplicação - 09 · Dicionários — CRUD, iteração, dict comprehension, aninhamento, merge - 10 · Strings em profundidade — métodos, f-strings, regex com módulo re - 11 · Algoritmos de ordenação e busca — bubble, merge sort, busca binária, Timsort - 12 · Recursão e estruturas avançadas — pilhas, filas, árvores binárias, heapq
Módulo 3 — Orientação a Objetos (13–18) - 13 · Classes e objetos — init, atributos, métodos, encapsulamento, @property - 14 · Herança e polimorfismo — super(), duck typing, classes abstratas, MRO - 15 · Interfaces, protocolos e composição — Protocol, Mixins, composição vs herança - 16 · Decoradores e metaprogramação — closures, @wraps, retry, singleton, dataclasses - 17 · Tratamento de exceções — try/except/else/finally, exceções customizadas, with - 18 · Módulos, pacotes e organização de projetos — imports, venv, pip, pyproject.toml
Módulo 4 — Arquivos, I/O e Banco de Dados (19–24) - 19 · Leitura e escrita de arquivos — open, pathlib, CSV, JSON, pickle - 20 · Trabalhando com datas e horas — datetime, timedelta, zoneinfo, dateutil - 21 · Banco de dados com SQLite e SQLAlchemy — CRUD, ORM, Repository, Alembic - 22 · MySQL, MariaDB, PostgreSQL e MongoDB — drivers, psycopg2, pymongo, aggregation - 23 · Consumindo APIs REST — requests, httpx, Session, retry, paginação, async - 24 · Concorrência — threading, multiprocessing, asyncio, ThreadPoolExecutor
Módulo 5 — Python para Web (25–30) - 25 · Flask — rotas, request, response, blueprints, middleware, configuração - 26 · FastAPI — Pydantic, CRUD, Depends, middleware, lifespan, BackgroundTasks - 27 · Autenticação e segurança — bcrypt, JWT, OAuth2, rate limiting, headers HTTP - 28 · Templates Jinja2 e Docker — herança de templates, Dockerfile, Docker Compose - 29 · WebSockets e tempo real — FastAPI WS, SSE, chat, notificações, autenticação WS - 30 · Celery — filas de tarefas, workers, beat scheduler, chain/group/chord, Flower
Módulo 6 — Automação e Scripts (31–34) - 31 · Automação de arquivos e SO — pathlib, shutil, subprocess, watchdog, argparse - 32 · Web scraping — BeautifulSoup, Selenium, paginação, Scrapy, monitor de preços - 33 · Planilhas e PDFs — openpyxl, pandas, ReportLab, PyMuPDF, pipeline de relatórios - 34 · E-mails e APIs de comunicação — smtplib, SendGrid, Twilio SMS/WhatsApp, Slack
Módulo 7 — Data Science e ML (35–40 de 42) - 35 · Introdução ao Data Science — NumPy, pandas, Matplotlib, Seaborn, SciPy, EDA - 36 · Machine Learning com scikit-learn — classificação, regressão, pipeline, GridSearchCV - 37 · Redes Neurais com TensorFlow/Keras — Sequential, Funcional, callbacks, transfer learning - 38 · NLP — NLTK, spaCy, TF-IDF, sentimentos, Word2Vec, Transformers, Hugging Face - 39 · Visão Computacional — Pillow, OpenCV, detecção de bordas, faces, OCR, vídeo - 40 · Pandas avançado e Plotly — merge, pivot, window functions, Dash, dashboards
O que ainda falta no Módulo 7
| Artigo | Tema |
|---|---|
| 41 | Séries Temporais e Previsão com Prophet e statsmodels |
| 42 | MLOps: versionamento de modelos, MLflow e deploy de ML |
Módulos ainda pendentes
Módulo 8 — Testes, Qualidade e Boas Práticas (43–47) - 43 · Testes com pytest — fixtures, parametrize, mocks, cobertura - 44 · Qualidade de código — linting, formatação, type hints, mypy - 45 · CI/CD com GitHub Actions — pipelines, testes automáticos, deploy - 46 · Logging, monitoramento e observabilidade - 47 · Design Patterns em Python
Módulo 9 — Projetos Reais e Carreira (48–52) - 48 · Projeto 1 — API REST completa com FastAPI + PostgreSQL + Docker - 49 · Projeto 2 — Pipeline de Data Science do zero ao deploy - 50 · Projeto 3 — Bot de automação com scraping + notificações - 51 · Projeto 4 — Dashboard analítico com Dash e banco de dados real - 52 · Carreira — portfólio, GitHub, contribuição open source, mercado Python
Estamos no Artigo 40 de 52 — faltam 12 artigos para concluir a série.
Digite "Sim" para continuar com o Artigo 41 — Séries Temporais e Previsão.
you asked
Sim