Toda análise de dados começa da mesma forma: você carrega um dataset e tenta entender o que há nele. Quantas linhas? Quais colunas são numéricas? Quanto está faltando? Alguma variável está extremamente distorcida? A maioria de nós responde essas perguntas copiando e colando os mesmos trechos de df.describe(), df.isna().sum() e df.groupby(...).agg(...) que já digitamos milhares de vezes.
Isso é esforço desperdiçado. O ecossistema Python hoje oferece ferramentas que levam você de um DataFrame bruto a uma tabela de resumo formatada e compartilhável em uma ou duas linhas — e outras desenvolvidas especificamente para produzir o tipo de “Tabela 1” que você vê em artigos científicos.
Este tutorial, baseado no artigo de Kanwal Mehreen no KDnuggets, percorre 7 passos para construir um pipeline repetível de estatísticas descritivas em Python. Usaremos o dataset Palmer Penguins ao longo do caminho — ele é pequeno, aberto e tem uma mistura realista de colunas numéricas e categóricas, valores ausentes reais e uma variável de agrupamento natural (species).
Pré-requisitos
| Componente | Mínimo | Recomendado |
|---|---|---|
| Python | 3.8+ | 3.11+ |
| Pandas | 1.5+ | 2.2+ |
| Pacotes adicionais | skimpy, tableone | great-tables, fg-data-profiling |
| Conhecimento prévio | Pandas básico | Estatística descritiva |
Passo 1: Configurando o ambiente e carregando os dados
Instale os pacotes que usaremos:
pip install pandas seaborn skimpy tableone great-tables fg-data-profilingNota importante sobre o último pacote: a popular biblioteca de profiling foi renomeada mais de uma vez. Começou como pandas-profiling, virou ydata-profiling em 2023 e foi renomeada novamente para fg-data-profiling em abril de 2026. Para novos projetos, prefira fg-data-profiling.
Agora carregue os dados. O Seaborn inclui o dataset Penguins nativamente:
import pandas as pd
import seaborn as sns
df = sns.load_dataset("penguins")
print(df.shape) # (344, 7)
print(df.dtypes) # 3 categóricas + 4 numéricas
print(df.isna().sum()) # 2 ausentes em medidas, 11 em sexO dataset tem sete colunas: três categóricas (species, island, sex) e quatro medições numéricas (bill_length_mm, bill_depth_mm, flipper_length_mm, body_mass_g). As medições têm 2 valores ausentes cada, e sex tem 11.
Passo 2: Linha de base com df.describe()
O describe() do Pandas é o ponto de partida óbvio — é instantâneo e não requer instalação adicional:
df.describe().round(2)O resultado mostra contagem, média, desvio padrão, mínimo, quartis e máximo para as 4 colunas numéricas. Útil, mas com pontos cegos importantes: ignora colunas categóricas, mostra contagem de valores não-nulos mas não a porcentagem de ausentes, e para no resumo de cinco números — sem assimetria, sem curtose, sem noção da forma da distribuição.
Passo 3: Expandindo o Pandas com include, .agg() e groupby
Para incluir categóricas no describe():
df.describe(include="all").round(2)Isso adiciona unique, top e freq para colunas categóricas. Para mais controle, use .agg() com dicionário:
df.agg({
"bill_length_mm": ["mean", "std", "skew"],
"body_mass_g": ["mean", "std", "min", "max"]
}).round(2)E para estatísticas por grupo:
df.groupby("species").agg({
"bill_length_mm": ["mean", "std"],
"body_mass_g": ["mean", "std"]
}).round(2)Isso já cobre 80% dos casos de uso. Mas a formatação ainda é bruta — você precisa copiar, colar e ajustar manualmente para um relatório.
Passo 4: Resumos elegantes com skimpy
O skimpy gera tabelas de resumo limpas e prontas para publicação com uma única função:
from skimpy import skim
skim(df)A saída inclui: tipo de dado, contagem, valores ausentes (contagem e %), média, desvio padrão, mínimo, máximo, e um mini-histograma inline no terminal para cada coluna numérica. Para categóricas, mostra contagem de categorias únicas e a categoria mais frequente.
O grande diferencial do skimpy é o mini-histograma inline — você identifica assimetria e outliers em um piscar de olhos, sem gerar gráficos separados.
Passo 5: Tabela 1 profissional com tableone
O tableone replica o padrão acadêmico da “Tabela 1” — a tabela de características basais da amostra que aparece em todo artigo científico:
from tableone import TableOne
table = TableOne(
df,
columns=["bill_length_mm", "bill_depth_mm",
"flipper_length_mm", "body_mass_g"],
categorical=["species", "island", "sex"],
groupby="species",
pval=True,
missing=False
)
print(table.tabulate(tablefmt="grid"))O tableone calcula automaticamente: média (DP) para variáveis contínuas, n (%) para categóricas, e valores-p para comparação entre grupos (ANOVA para contínuas, qui-quadrado para categóricas). Perfeito para papers e relatórios clínicos.
Passo 6: Tabelas formatadas em HTML com great-tables
O great-tables (do ecossistema Posit/RStudio) leva a formatação para outro nível, gerando tabelas HTML com estilo profissional:
from great_tables import GT
# Criar tabela de resumo por espécie
summary = df.groupby("species").agg(
count=("body_mass_g", "count"),
mean_mass=("body_mass_g", "mean"),
sd_mass=("body_mass_g", "std"),
mean_bill=("bill_length_mm", "mean"),
sd_bill=("bill_length_mm", "std")
).reset_index()
gt = GT(summary)
gt.tab_header(title="Penguins: Resumo por Espécie")
gt.fmt_number(columns=["mean_mass", "sd_mass", "mean_bill", "sd_bill"], decimals=1)
gt.show() # Renderiza HTMLO great-tables é ideal para incluir tabelas diretamente em dashboards Streamlit, relatórios Quarto ou notebooks Jupyter.
Passo 7: Perfil completo automatizado com fg-data-profiling
Para o relatório mais completo — uma página HTML interativa com tudo: correlações, distribuições, missing values, alerts — use o fg-data-profiling:
from fg_data_profiling import ProfileReport
profile = ProfileReport(df, title="Penguins Dataset - Profiling Report")
profile.to_file("penguins_report.html")O relatório gerado inclui: visão geral do dataset, análise de cada variável (histograma, valores distintos, missing), correlações (Pearson, Spearman, Cramér’s V), alertas automáticos (alta cardinalidade, missing values, skewness, correlação alta) e amostra dos dados.
Tabela comparativa das ferramentas
| Ferramenta | Melhor para | Saída | Curva de aprendizado |
|---|---|---|---|
| df.describe() | Check rápido no console | Texto | Zero |
| skimpy | Resumo visual no terminal | Texto + histogramas | Baixa |
| tableone | Tabela 1 acadêmica | Texto formatado | Média |
| great-tables | Tabelas HTML profissionais | HTML | Média |
| fg-data-profiling | Relatório interativo completo | HTML | Baixa |
Pipeline completo em uma função
Juntando tudo em uma função reutilizável:
def descriptive_pipeline(df, groupby=None, output_dir="reports"):
# Gera relatório completo de estatísticas descritivas
import osos.makedirs(output_dir, exist_ok=True)
# 1. Linha de base
print(“=== df.describe() ===”)
print(df.describe(include=”all”).round(2))
# 2. skimpy
from skimpy import skim
print(”
=== skimpy ===”)
skim(df)
# 3. tableone com grupo
if groupby:
from tableone import TableOne
num_cols = df.select_dtypes(“number”).columns.tolist()
cat_cols = df.select_dtypes([“object”, “category”]).columns.tolist()
t1 = TableOne(df, columns=num_cols, categorical=cat_cols,
groupby=groupby, pval=True)
t1.to_csv(f”{output_dir}/tableone.csv”)
print(f”
Tabela 1 salva em {output_dir}/tableone.csv”)
# 4. Relatório completo
from fg_data_profiling import ProfileReport
profile = ProfileReport(df, title=”Relatório Descritivo”)
profile.to_file(f”{output_dir}/report.html”)
print(f”Relatório salvo em {output_dir}/report.html”)
# Uso
descriptive_pipeline(df, groupby=”species”)
Troubleshooting
- ❌ Erro ao instalar fg-data-profiling: o pacote foi renomeado recentemente. Tente
pip install ydata-profilingcomo fallback (versão anterior, ainda funcional). - ❌ skimpy não mostra histogramas: verifique se seu terminal suporta Unicode. No Windows, use o novo Terminal ou PowerShell 7+.
- ❌ tableone quebra com colunas não numéricas: o parâmetro
columnsdeve conter apenas colunas numéricas. Usedf.select_dtypes("number").columnspara filtrar. - ❌ MemoryError com datasets grandes: o fg-data-profiling processa o dataset inteiro na memória. Para datasets > 1M de linhas, use
ProfileReport(df.sample(100000)). - ❌ Valores-p do tableone dão NaN: ocorre quando um grupo tem variância zero ou tamanho muito pequeno. Verifique com
df.groupby(grupo).size().
FAQ
Preciso de todas essas ferramentas? Não. Comece com df.describe() + skimpy para o dia a dia. Adicione tableone se publicar papers. Adicione fg-data-profiling para relatórios automáticos.
Qual a diferença entre skimpy e fg-data-profiling? Skimpy é rápido, textual e roda no terminal. fg-data-profiling gera um relatório HTML interativo completo — use quando precisar compartilhar resultados com não-técnicos.
Funciona com PySpark ou Polars? tableone e fg-data-profiling exigem Pandas. Para Polars, converta com .to_pandas(). Para PySpark, use summary() nativo.
Dá para automatizar isso em CI/CD? Sim. O pipeline pode rodar em GitHub Actions após cada atualização de dados, gerando relatórios como artifacts.
Preciso aprender great-tables se já uso matplotlib/seaborn? São propósitos diferentes. great-tables gera tabelas formatadas, não gráficos. Use junto com matplotlib para combinar visualizações e tabelas no mesmo relatório.
Descubra mais sobre noticiAI
Assine para receber nossas notícias mais recentes por e-mail.



