<h2>Introdução ao Pandas: Estrutura e Conceitos Fundamentais</h2>
<p>O Pandas é a biblioteca mais utilizada em Python para manipulação e análise de dados. Ele fornece estruturas de dados poderosas e ferramentas que tornam o trabalho com dados tabulares (como planilhas Excel ou bases de dados) intuitivo e eficiente. Quando você trabalha com dados no mundo real, raramente encontra informações limpas e organizadas — é aqui que o Pandas se torna indispensável.</p>
<p>A filosofia do Pandas é trazer a experiência familiar de trabalhar com DataFrames (similar aos data frames do R) para o ecossistema Python. Você carrega dados de diversas fontes, executa transformações complexas com poucas linhas de código e extrai insights rapidamente. Neste artigo, vamos percorrer desde a criação básica de estruturas até técnicas avançadas de análise exploratória.</p>
<h2>DataFrames: A Coluna Vertebral do Pandas</h2>
<h3>O que é um DataFrame?</h3>
<p>Um DataFrame é uma estrutura de dados bidimensional com linhas e colunas, similar a uma tabela em um banco de dados ou uma planilha. Cada coluna pode ter um tipo de dado diferente (números, strings, datas, etc.), e há um índice que identifica uniquely cada linha. Diferentemente de uma lista de listas, um DataFrame oferece operações vetorizadas que são muito mais rápidas.</p>
<pre><code class="language-python">import pandas as pd
import numpy as np
Criando um DataFrame a partir de um dicionário
dados = {
'nome': ['Alice', 'Bruno', 'Carlos', 'Diana'],
'idade': [28, 34, 29, 31],
'salario': [5000, 7500, 6200, 6800],
'departamento': ['TI', 'RH', 'TI', 'Financeiro']
}
df = pd.DataFrame(dados)
print(df)</code></pre>
<p>Output:</p>
<pre><code> nome idade salario departamento
0 Alice 28 5000 TI
1 Bruno 34 7500 RH
2 Carlos 29 6200 TI
3 Diana 31 6800 Financeiro</code></pre>
<h3>Inspecionando DataFrames</h3>
<p>Antes de iniciar qualquer análise, você precisa entender a estrutura dos seus dados. As funções <code>info()</code>, <code>describe()</code> e <code>shape</code> são suas melhores amigas. A função <code>info()</code> mostra tipos de dados e valores ausentes, enquanto <code>describe()</code> apresenta estatísticas descritivas para colunas numéricas.</p>
<pre><code class="language-python"># Informações gerais sobre o DataFrame
print(df.info())
Output mostra: tipos de dados, não-nulos, uso de memória
Estatísticas descritivas
print(df.describe())
Output inclui: count, mean, std, min, 25%, 50%, 75%, max
Dimensões
print(f"Linhas: {df.shape[0]}, Colunas: {df.shape[1]}")
Output: Linhas: 4, Colunas: 4
Primeiras e últimas linhas
print(df.head(2)) # Primeiras 2 linhas
print(df.tail(1)) # Última linha</code></pre>
<h3>Seleção e Indexação</h3>
<p>Acessar dados específicos em um DataFrame é fundamental. Python oferece várias formas de fazer isso, e escolher a correta muda tudo em termos de desempenho e legibilidade.</p>
<pre><code class="language-python"># Seleção por nome de coluna (retorna Series)
print(df['nome'])
Seleção de múltiplas colunas (retorna DataFrame)
print(df[['nome', 'salario']])
Seleção por posição (iloc - integer location)
print(df.iloc[0]) # Primeira linha
print(df.iloc[1, 2]) # Segunda linha, terceira coluna (valor 7500)
Seleção por rótulo (loc - label-based)
print(df.loc[0, 'nome']) # Linha 0, coluna 'nome' (valor 'Alice')
Seleção condicional (booleana)
maiores_30 = df[df['idade'] > 30]
print(maiores_30)</code></pre>
<h2>Limpeza de Dados: Tratamento de Valores Ausentes e Inconsistências</h2>
<h3>Identificação e Tratamento de Valores Ausentes</h3>
<p>Dados do mundo real frequentemente contêm valores ausentes (NaN, None, strings vazias). Deixá-los sem tratamento compromete análises posteriores. O primeiro passo é identificar onde eles estão e decidir como lidar com eles.</p>
<pre><code class="language-python"># Criando um DataFrame com dados ausentes
dados_incompletos = {
'produto': ['A', 'B', None, 'D', 'E'],
'venda': [100, np.nan, 150, 200, None],
'categoria': ['X', 'Y', 'X', None, 'Y']
}
df_limpo = pd.DataFrame(dados_incompletos)
Encontrando valores ausentes
print(df_limpo.isnull()) # DataFrame booleano
print(df_limpo.isnull().sum()) # Contagem por coluna
Removendo linhas com qualquer valor ausente
df_sem_nulos = df_limpo.dropna()
Removendo linhas onde coluna específica é nula
df_sem_nulos_venda = df_limpo.dropna(subset=['venda'])
Preenchendo valores ausentes
df_preenchido = df_limpo.fillna({'venda': 0, 'categoria': 'Indefinido'})
print(df_preenchido)</code></pre>
<h3>Detecção e Correção de Duplicatas</h3>
<p>Duplicatas podem distorcer análises e devem ser identificadas cuidadosamente. Nem sempre você quer removê-las — às vezes, apenas registrá-las é necessário.</p>
<pre><code class="language-python"># Criando dados com duplicatas
dados_dup = {
'id': [1, 2, 2, 3, 1],
'nome': ['Alice', 'Bruno', 'Bruno', 'Carlos', 'Alice'],
'email': ['alice@email.com', 'bruno@email.com', 'bruno@email.com', 'carlos@email.com', 'alice@email.com']
}
df_dup = pd.DataFrame(dados_dup)
Identificando duplicatas (True para duplicadas, False para primeira ocorrência)
print(df_dup.duplicated())
Contando duplicatas
duplicatas = df_dup.duplicated().sum()
print(f"Total de duplicatas: {duplicatas}")
Removendo duplicatas (mantém primeira ocorrência por padrão)
df_unico = df_dup.drop_duplicates()
Removendo duplicatas considerando apenas colunas específicas
df_unico_id = df_dup.drop_duplicates(subset=['id'], keep='first')
print(df_unico_id)</code></pre>
<h3>Transformação de Tipos de Dados</h3>
<p>Colunas frequentemente chegam com tipos incorretos. Uma coluna que deveria ser data chega como string, ou um ID numérico chega como inteiro quando deveria ser categórico. Corrigir isso é essencial.</p>
<pre><code class="language-python"># Exemplo de dados com tipos incorretos
dados_tipos = {
'data': ['2024-01-15', '2024-02-20', '2024-03-10'],
'preco': ['100.50', '250.00', '75.99'],
'categoria_id': [1, 2, 1]
}
df_tipos = pd.DataFrame(dados_tipos)
Verificando tipos atuais
print(df_tipos.dtypes)
Convertendo string para datetime
df_tipos['data'] = pd.to_datetime(df_tipos['data'])
Convertendo string para float
df_tipos['preco'] = df_tipos['preco'].astype(float)
Convertendo inteiro para categórico
df_tipos['categoria_id'] = df_tipos['categoria_id'].astype('category')
print(df_tipos.dtypes)
print(df_tipos)</code></pre>
<h2>Análise Exploratória de Dados (EDA)</h2>
<h3>Operações Grupais e Agregação</h3>
<p>Agrupar dados por uma ou mais colunas e aplicar operações agregadas é uma tarefa comum. Isso permite resumir grandes volumes de dados em informações significativas.</p>
<pre><code class="language-python"># Dados de vendas
vendas = {
'departamento': ['TI', 'TI', 'RH', 'RH', 'TI', 'Financeiro'],
'mes': [1, 2, 1, 2, 1, 1],
'valor': [5000, 6000, 3000, 3500, 7000, 4500]
}
df_vendas = pd.DataFrame(vendas)
Agrupando por departamento e somando valores
resumo = df_vendas.groupby('departamento')['valor'].sum()
print(resumo)
Múltiplas agregações
resumo_multi = df_vendas.groupby('departamento').agg({
'valor': ['sum', 'mean', 'count', 'std']
})
print(resumo_multi)
Agrupando por múltiplas colunas
resumo_duplo = df_vendas.groupby(['departamento', 'mes'])['valor'].sum()
print(resumo_duplo)
Criando novos nomes para colunas agregadas
resumo_nomes = df_vendas.groupby('departamento')['valor'].agg(
total='sum',
media='mean',
quantidade='count'
)
print(resumo_nomes)</code></pre>
<h3>Estatísticas Descritivas e Correlações</h3>
<p>Compreender o comportamento estatístico dos seus dados revela padrões importantes. Correlações indicam relacionamentos entre variáveis que podem ser investigados mais profundamente.</p>
<pre><code class="language-python"># Dados de desempenho de estudantes
performance = {
'hora_estudo': [2, 5, 3, 6, 4, 7, 2.5],
'nota_prova': [50, 85, 60, 90, 70, 95, 55],
'frequencia': [0.7, 0.95, 0.8, 0.98, 0.75, 0.99, 0.6]
}
df_perf = pd.DataFrame(performance)
Estatísticas descritivas completas
print(df_perf.describe())
Correlação entre variáveis (matriz de correlação)
correlacao = df_perf.corr()
print(correlacao)
Correlação de Pearson entre duas colunas específicas
pearson = df_perf['hora_estudo'].corr(df_perf['nota_prova'])
print(f"Correlação de Pearson: {pearson:.3f}")
Desvio padrão por coluna
desvios = df_perf.std()
print(desvios)
Quantis (percentis)
quantis = df_perf.quantile([0.25, 0.5, 0.75])
print(quantis)</code></pre>
<h3>Transformação e Criação de Novas Colunas</h3>
<p>Frequentemente você precisa criar novas colunas derivadas de dados existentes. Isso pode ser uma simples multiplicação ou uma lógica condicional complexa.</p>
<pre><code class="language-python"># Dados de e-commerce
pedidos = {
'produto': ['Notebook', 'Mouse', 'Teclado', 'Monitor', 'Webcam'],
'preco_unitario': [3000, 50, 200, 800, 150],
'quantidade': [2, 10, 5, 1, 3],
'desconto_percentual': [0.1, 0, 0.05, 0.15, 0]
}
df_pedidos = pd.DataFrame(pedidos)
Criando coluna de valor bruto
df_pedidos['valor_bruto'] = df_pedidos['preco_unitario'] * df_pedidos['quantidade']
Criando coluna de desconto em reais
df_pedidos['desconto_reais'] = df_pedidos['valor_bruto'] * df_pedidos['desconto_percentual']
Criando coluna de valor líquido
df_pedidos['valor_liquido'] = df_pedidos['valor_bruto'] - df_pedidos['desconto_reais']
Lógica condicional com np.where
df_pedidos['categoria_preco'] = np.where(
df_pedidos['preco_unitario'] > 500,
'Premium',
'Padrão'
)
Lógica condicional mais complexa com pd.cut (categorização por bins)
df_pedidos['faixa_valor'] = pd.cut(
df_pedidos['valor_liquido'],
bins=[0, 500, 2000, float('inf')],
labels=['Baixo', 'Médio', 'Alto']
)
print(df_pedidos)</code></pre>
<h3>Análise de Distribuições e Outliers</h3>
<p>Identificar valores extremos (outliers) é crítico para evitar conclusões enviesadas. Nem sempre outliers são erros — às vezes são observações legítimas e importantes.</p>
<pre><code class="language-python"># Dados de salários
salarios = {
'area': ['TI', 'TI', 'TI', 'TI', 'TI', 'RH', 'RH', 'RH', 'RH'],
'salario': [5000, 5200, 5100, 5300, 25000, 3000, 3200, 3100, 3150]
}
df_sal = pd.DataFrame(salarios)
Calculando quartis e IQR (Interquartile Range)
Q1 = df_sal['salario'].quantile(0.25)
Q3 = df_sal['salario'].quantile(0.75)
IQR = Q3 - Q1
Limites para outliers (método IQR)
limite_inferior = Q1 - 1.5 * IQR
limite_superior = Q3 + 1.5 * IQR
Identificando outliers
df_sal['outlier'] = (df_sal['salario'] < limite_inferior) | (df_sal['salario'] > limite_superior)
print(f"Q1: {Q1}, Q3: {Q3}, IQR: {IQR}")
print(f"Limites: [{limite_inferior}, {limite_superior}]")
print(df_sal[df_sal['outlier']])
Calculando escore Z (quantos desvios padrão do mean)
from scipy import stats
df_sal['z_score'] = np.abs(stats.zscore(df_sal['salario']))
outliers_z = df_sal[df_sal['z_score'] > 3]
print(outliers_z)</code></pre>
<h3>Reformatação de Dados com Pivot e Melt</h3>
<p>Às vezes você precisa reorganizar dados de forma radicalmente diferente. Essas operações (pivot e melt) transformam a estrutura do DataFrame de forma eficiente.</p>
<pre><code class="language-python"># Dados em formato longo (tidy)
vendas_longo = {
'loja': ['Loja A', 'Loja A', 'Loja A', 'Loja B', 'Loja B', 'Loja B'],
'mes': [1, 2, 3, 1, 2, 3],
'valor': [10000, 12000, 11500, 8000, 9500, 9800]
}
df_longo = pd.DataFrame(vendas_longo)
Convertendo para formato largo com pivot
df_largo = df_longo.pivot(index='loja', columns='mes', values='valor')
print("Formato Largo:")
print(df_largo)
Adicionando sufixo às colunas
df_largo.columns.name = None
df_largo = df_largo.rename(columns={1: 'jan', 2: 'fev', 3: 'mar'})
Convertendo de volta para formato longo com melt
df_revertido = df_largo.reset_index().melt(id_vars='loja', var_name='mes', value_name='valor')
print("\nFormato Longo (após melt):")
print(df_revertido)</code></pre>
<h2>Análise Prática: Case Completo</h2>
<p>Para consolidar os conceitos, vamos executar uma análise exploratória completa em um dataset simulado de e-commerce.</p>
<pre><code class="language-python"># Criando dataset realista
np.random.seed(42)
n_transacoes = 100
dados_ecommerce = {
'data': pd.date_range('2024-01-01', periods=n_transacoes, freq='D'),
'cliente_id': np.random.randint(1000, 1050, n_transacoes),
'categoria': np.random.choice(['Eletrônicos', 'Moda', 'Livros', 'Casa'], n_transacoes),
'valor_transacao': np.random.uniform(50, 500, n_transacoes),
'metodo_pagamento': np.random.choice(['Cartão', 'PIX', 'Boleto'], n_transacoes)
}
df_ecommerce = pd.DataFrame(dados_ecommerce)
1. Limpeza: adicionar alguns nulos propositalmente e limpar
df_ecommerce.loc[np.random.choice(df_ecommerce.index, 5, replace=False), 'cliente_id'] = np.nan
df_ecommerce = df_ecommerce.dropna(subset=['cliente_id'])
2. Transformação: arredondar valores, criar semana
df_ecommerce['valor_transacao'] = df_ecommerce['valor_transacao'].round(2)
df_ecommerce['semana'] = df_ecommerce['data'].dt.isocalendar().week
df_ecommerce['mes'] = df_ecommerce['data'].dt.month
3. Análise exploratória
print("=== RESUMO DO DATASET ===")
print(f"Total de transações: {len(df_ecommerce)}")
print(f"Período: {df_ecommerce['data'].min().date()} a {df_ecommerce['data'].max().date()}")
print(f"\nValor médio de transação: R$ {df_ecommerce['valor_transacao'].mean():.2f}")
print(f"Valor total: R$ {df_ecommerce['valor_transacao'].sum():.2f}")
print("\n=== ANÁLISE POR CATEGORIA ===")
por_categoria = df_ecommerce.groupby('categoria').agg({
'valor_transacao': ['sum', 'mean', 'count']
}).round(2)
por_categoria.columns = ['Total (R$)', 'Ticket Médio (R$)', 'Quantidade']
print(por_categoria)
print("\n=== MÉTODO DE PAGAMENTO ===")
por_pagamento = df_ecommerce['metodo_pagamento'].value_counts()
print(por_pagamento)
print("\n=== CLIENTES ===")
clientes_unicos = df_ecommerce['cliente_id'].nunique()
print(f"Total de clientes únicos: {clientes_unicos}")
Cliente que mais gastou
cliente_maior_gasto = df_ecommerce.groupby('cliente_id')['valor_transacao'].sum().idxmax()
maior_gasto = df_ecommerce.groupby('cliente_id')['valor_transacao'].sum().max()
print(f"Cliente com maior gasto: ID {int(cliente_maior_gasto)} (R$ {maior_gasto:.2f})")</code></pre>
<h2>Conclusão</h2>
<p>Neste artigo, você aprendeu os três pilares da análise de dados com Pandas: primeiro, como estruturar e explorar DataFrames através de operações de seleção e inspeção; segundo, como identificar e corrigir problemas comuns em dados reais como valores ausentes, duplicatas e tipos incorretos; terceiro, como extrair insights através de agregações, estatísticas e transformações de dados. A prática regular com esses conceitos — começando em datasets pequenos e evoluindo para maiores — é o caminho mais seguro para dominar a bibliotec. O Pandas não é apenas uma ferramenta: é o idioma padrão para comunicar-se com dados em Python.</p>
<h2>Referências</h2>
<ol>
<li><a href="https://pandas.pydata.org/docs/" target="_blank" rel="noopener noreferrer">Pandas Official Documentation</a></li>
<li><a href="https://wesmckinney.com/book/" target="_blank" rel="noopener noreferrer">Python for Data Analysis - Wes McKinney (Livro)</a></li>
<li><a href="https://realpython.com/learning-paths/pandas-data-science/" target="_blank" rel="noopener noreferrer">Real Python - Pandas Tutorials</a></li>
<li><a href="https://www.kaggle.com/learn/pandas" target="_blank" rel="noopener noreferrer">Kaggle - Pandas Guide</a></li>
<li><a href="https://towardsdatascience.com/exploratory-data-analysis-eda-dont-ask-how-to-start-46988c60127b" target="_blank" rel="noopener noreferrer">Medium - Exploratory Data Analysis with Pandas</a></li>
</ol>
<p><!-- FIM --></p>