Python

Guia Completo de Pandas em Python: DataFrames, Limpeza de Dados e Análise Exploratória

15 min de leitura

Guia Completo de Pandas em Python: DataFrames, Limpeza de Dados e Análise Exploratória

Introdução ao Pandas: Estrutura e Conceitos Fundamentais 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. 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. DataFrames: A Coluna Vertebral do Pandas O que é um DataFrame? Um DataFrame é uma estrutura de dados bidimensional com linhas e colunas, similar a uma tabela em um banco de dados ou

<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 = {

&#039;nome&#039;: [&#039;Alice&#039;, &#039;Bruno&#039;, &#039;Carlos&#039;, &#039;Diana&#039;],

&#039;idade&#039;: [28, 34, 29, 31],

&#039;salario&#039;: [5000, 7500, 6200, 6800],

&#039;departamento&#039;: [&#039;TI&#039;, &#039;RH&#039;, &#039;TI&#039;, &#039;Financeiro&#039;]

}

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&quot;Linhas: {df.shape[0]}, Colunas: {df.shape[1]}&quot;)

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[&#039;nome&#039;])

Seleção de múltiplas colunas (retorna DataFrame)

print(df[[&#039;nome&#039;, &#039;salario&#039;]])

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, &#039;nome&#039;]) # Linha 0, coluna &#039;nome&#039; (valor &#039;Alice&#039;)

Seleção condicional (booleana)

maiores_30 = df[df[&#039;idade&#039;] &gt; 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 = {

&#039;produto&#039;: [&#039;A&#039;, &#039;B&#039;, None, &#039;D&#039;, &#039;E&#039;],

&#039;venda&#039;: [100, np.nan, 150, 200, None],

&#039;categoria&#039;: [&#039;X&#039;, &#039;Y&#039;, &#039;X&#039;, None, &#039;Y&#039;]

}

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=[&#039;venda&#039;])

Preenchendo valores ausentes

df_preenchido = df_limpo.fillna({&#039;venda&#039;: 0, &#039;categoria&#039;: &#039;Indefinido&#039;})

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 = {

&#039;id&#039;: [1, 2, 2, 3, 1],

&#039;nome&#039;: [&#039;Alice&#039;, &#039;Bruno&#039;, &#039;Bruno&#039;, &#039;Carlos&#039;, &#039;Alice&#039;],

&#039;email&#039;: [&#039;alice@email.com&#039;, &#039;bruno@email.com&#039;, &#039;bruno@email.com&#039;, &#039;carlos@email.com&#039;, &#039;alice@email.com&#039;]

}

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&quot;Total de duplicatas: {duplicatas}&quot;)

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=[&#039;id&#039;], keep=&#039;first&#039;)

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 = {

&#039;data&#039;: [&#039;2024-01-15&#039;, &#039;2024-02-20&#039;, &#039;2024-03-10&#039;],

&#039;preco&#039;: [&#039;100.50&#039;, &#039;250.00&#039;, &#039;75.99&#039;],

&#039;categoria_id&#039;: [1, 2, 1]

}

df_tipos = pd.DataFrame(dados_tipos)

Verificando tipos atuais

print(df_tipos.dtypes)

Convertendo string para datetime

df_tipos[&#039;data&#039;] = pd.to_datetime(df_tipos[&#039;data&#039;])

Convertendo string para float

df_tipos[&#039;preco&#039;] = df_tipos[&#039;preco&#039;].astype(float)

Convertendo inteiro para categórico

df_tipos[&#039;categoria_id&#039;] = df_tipos[&#039;categoria_id&#039;].astype(&#039;category&#039;)

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 = {

&#039;departamento&#039;: [&#039;TI&#039;, &#039;TI&#039;, &#039;RH&#039;, &#039;RH&#039;, &#039;TI&#039;, &#039;Financeiro&#039;],

&#039;mes&#039;: [1, 2, 1, 2, 1, 1],

&#039;valor&#039;: [5000, 6000, 3000, 3500, 7000, 4500]

}

df_vendas = pd.DataFrame(vendas)

Agrupando por departamento e somando valores

resumo = df_vendas.groupby(&#039;departamento&#039;)[&#039;valor&#039;].sum()

print(resumo)

Múltiplas agregações

resumo_multi = df_vendas.groupby(&#039;departamento&#039;).agg({

&#039;valor&#039;: [&#039;sum&#039;, &#039;mean&#039;, &#039;count&#039;, &#039;std&#039;]

})

print(resumo_multi)

Agrupando por múltiplas colunas

resumo_duplo = df_vendas.groupby([&#039;departamento&#039;, &#039;mes&#039;])[&#039;valor&#039;].sum()

print(resumo_duplo)

Criando novos nomes para colunas agregadas

resumo_nomes = df_vendas.groupby(&#039;departamento&#039;)[&#039;valor&#039;].agg(

total=&#039;sum&#039;,

media=&#039;mean&#039;,

quantidade=&#039;count&#039;

)

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 = {

&#039;hora_estudo&#039;: [2, 5, 3, 6, 4, 7, 2.5],

&#039;nota_prova&#039;: [50, 85, 60, 90, 70, 95, 55],

&#039;frequencia&#039;: [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[&#039;hora_estudo&#039;].corr(df_perf[&#039;nota_prova&#039;])

print(f&quot;Correlação de Pearson: {pearson:.3f}&quot;)

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 = {

&#039;produto&#039;: [&#039;Notebook&#039;, &#039;Mouse&#039;, &#039;Teclado&#039;, &#039;Monitor&#039;, &#039;Webcam&#039;],

&#039;preco_unitario&#039;: [3000, 50, 200, 800, 150],

&#039;quantidade&#039;: [2, 10, 5, 1, 3],

&#039;desconto_percentual&#039;: [0.1, 0, 0.05, 0.15, 0]

}

df_pedidos = pd.DataFrame(pedidos)

Criando coluna de valor bruto

df_pedidos[&#039;valor_bruto&#039;] = df_pedidos[&#039;preco_unitario&#039;] * df_pedidos[&#039;quantidade&#039;]

Criando coluna de desconto em reais

df_pedidos[&#039;desconto_reais&#039;] = df_pedidos[&#039;valor_bruto&#039;] * df_pedidos[&#039;desconto_percentual&#039;]

Criando coluna de valor líquido

df_pedidos[&#039;valor_liquido&#039;] = df_pedidos[&#039;valor_bruto&#039;] - df_pedidos[&#039;desconto_reais&#039;]

Lógica condicional com np.where

df_pedidos[&#039;categoria_preco&#039;] = np.where(

df_pedidos[&#039;preco_unitario&#039;] &gt; 500,

&#039;Premium&#039;,

&#039;Padrão&#039;

)

Lógica condicional mais complexa com pd.cut (categorização por bins)

df_pedidos[&#039;faixa_valor&#039;] = pd.cut(

df_pedidos[&#039;valor_liquido&#039;],

bins=[0, 500, 2000, float(&#039;inf&#039;)],

labels=[&#039;Baixo&#039;, &#039;Médio&#039;, &#039;Alto&#039;]

)

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 = {

&#039;area&#039;: [&#039;TI&#039;, &#039;TI&#039;, &#039;TI&#039;, &#039;TI&#039;, &#039;TI&#039;, &#039;RH&#039;, &#039;RH&#039;, &#039;RH&#039;, &#039;RH&#039;],

&#039;salario&#039;: [5000, 5200, 5100, 5300, 25000, 3000, 3200, 3100, 3150]

}

df_sal = pd.DataFrame(salarios)

Calculando quartis e IQR (Interquartile Range)

Q1 = df_sal[&#039;salario&#039;].quantile(0.25)

Q3 = df_sal[&#039;salario&#039;].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[&#039;outlier&#039;] = (df_sal[&#039;salario&#039;] &lt; limite_inferior) | (df_sal[&#039;salario&#039;] &gt; limite_superior)

print(f&quot;Q1: {Q1}, Q3: {Q3}, IQR: {IQR}&quot;)

print(f&quot;Limites: [{limite_inferior}, {limite_superior}]&quot;)

print(df_sal[df_sal[&#039;outlier&#039;]])

Calculando escore Z (quantos desvios padrão do mean)

from scipy import stats

df_sal[&#039;z_score&#039;] = np.abs(stats.zscore(df_sal[&#039;salario&#039;]))

outliers_z = df_sal[df_sal[&#039;z_score&#039;] &gt; 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 = {

&#039;loja&#039;: [&#039;Loja A&#039;, &#039;Loja A&#039;, &#039;Loja A&#039;, &#039;Loja B&#039;, &#039;Loja B&#039;, &#039;Loja B&#039;],

&#039;mes&#039;: [1, 2, 3, 1, 2, 3],

&#039;valor&#039;: [10000, 12000, 11500, 8000, 9500, 9800]

}

df_longo = pd.DataFrame(vendas_longo)

Convertendo para formato largo com pivot

df_largo = df_longo.pivot(index=&#039;loja&#039;, columns=&#039;mes&#039;, values=&#039;valor&#039;)

print(&quot;Formato Largo:&quot;)

print(df_largo)

Adicionando sufixo às colunas

df_largo.columns.name = None

df_largo = df_largo.rename(columns={1: &#039;jan&#039;, 2: &#039;fev&#039;, 3: &#039;mar&#039;})

Convertendo de volta para formato longo com melt

df_revertido = df_largo.reset_index().melt(id_vars=&#039;loja&#039;, var_name=&#039;mes&#039;, value_name=&#039;valor&#039;)

print(&quot;\nFormato Longo (após melt):&quot;)

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 = {

&#039;data&#039;: pd.date_range(&#039;2024-01-01&#039;, periods=n_transacoes, freq=&#039;D&#039;),

&#039;cliente_id&#039;: np.random.randint(1000, 1050, n_transacoes),

&#039;categoria&#039;: np.random.choice([&#039;Eletrônicos&#039;, &#039;Moda&#039;, &#039;Livros&#039;, &#039;Casa&#039;], n_transacoes),

&#039;valor_transacao&#039;: np.random.uniform(50, 500, n_transacoes),

&#039;metodo_pagamento&#039;: np.random.choice([&#039;Cartão&#039;, &#039;PIX&#039;, &#039;Boleto&#039;], 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), &#039;cliente_id&#039;] = np.nan

df_ecommerce = df_ecommerce.dropna(subset=[&#039;cliente_id&#039;])

2. Transformação: arredondar valores, criar semana

df_ecommerce[&#039;valor_transacao&#039;] = df_ecommerce[&#039;valor_transacao&#039;].round(2)

df_ecommerce[&#039;semana&#039;] = df_ecommerce[&#039;data&#039;].dt.isocalendar().week

df_ecommerce[&#039;mes&#039;] = df_ecommerce[&#039;data&#039;].dt.month

3. Análise exploratória

print(&quot;=== RESUMO DO DATASET ===&quot;)

print(f&quot;Total de transações: {len(df_ecommerce)}&quot;)

print(f&quot;Período: {df_ecommerce[&#039;data&#039;].min().date()} a {df_ecommerce[&#039;data&#039;].max().date()}&quot;)

print(f&quot;\nValor médio de transação: R$ {df_ecommerce[&#039;valor_transacao&#039;].mean():.2f}&quot;)

print(f&quot;Valor total: R$ {df_ecommerce[&#039;valor_transacao&#039;].sum():.2f}&quot;)

print(&quot;\n=== ANÁLISE POR CATEGORIA ===&quot;)

por_categoria = df_ecommerce.groupby(&#039;categoria&#039;).agg({

&#039;valor_transacao&#039;: [&#039;sum&#039;, &#039;mean&#039;, &#039;count&#039;]

}).round(2)

por_categoria.columns = [&#039;Total (R$)&#039;, &#039;Ticket Médio (R$)&#039;, &#039;Quantidade&#039;]

print(por_categoria)

print(&quot;\n=== MÉTODO DE PAGAMENTO ===&quot;)

por_pagamento = df_ecommerce[&#039;metodo_pagamento&#039;].value_counts()

print(por_pagamento)

print(&quot;\n=== CLIENTES ===&quot;)

clientes_unicos = df_ecommerce[&#039;cliente_id&#039;].nunique()

print(f&quot;Total de clientes únicos: {clientes_unicos}&quot;)

Cliente que mais gastou

cliente_maior_gasto = df_ecommerce.groupby(&#039;cliente_id&#039;)[&#039;valor_transacao&#039;].sum().idxmax()

maior_gasto = df_ecommerce.groupby(&#039;cliente_id&#039;)[&#039;valor_transacao&#039;].sum().max()

print(f&quot;Cliente com maior gasto: ID {int(cliente_maior_gasto)} (R$ {maior_gasto:.2f})&quot;)</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>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Python

Django em Python: MVT, ORM, Admin e Estrutura de Projetos: Do Básico ao Avançado
Django em Python: MVT, ORM, Admin e Estrutura de Projetos: Do Básico ao Avançado

Entendendo o MVT: A Arquitetura do Django Django segue um padrão arquitetural...

Guia Completo de Observabilidade em Python: OpenTelemetry, Sentry e Profiling com py-spy
Guia Completo de Observabilidade em Python: OpenTelemetry, Sentry e Profiling com py-spy

O Que é Observabilidade e Por Que Você Precisa Disso Observabilidade é a capa...

O que Todo Dev Deve Saber sobre Web Scraping em Python: requests, BeautifulSoup e Selenium
O que Todo Dev Deve Saber sobre Web Scraping em Python: requests, BeautifulSoup e Selenium

O que é Web Scraping e Por Que Aprender Web scraping é a técnica de extrair d...