Python Data Science: as 9 etapas-chave para passar de zero a operacional

Python Data Science: o essencial em um artigo — código real, diagramas e etapas concretas, extraídos de um curso de 36 lições.

Python Data Science: as 9 etapas-chave para passar de zero a operacional

Todo mundo pode aprender Python Data Science — desde que siga as etapas na ordem correta. Condensamos um curso completo de 36 lições em um percurso claro, com os trechos de código mais úteis.

tl;dr
  • Introdução e Instalação
  • NumPy essencial
  • Pandas Series e DataFrames
  • Leitura e escrita de dados
  • Limpeza de dados
~$ cat ./parcours.md # Python Data Science — 10 capítulos
01
Introdução e Instalação
→ Apresentação do curso→ Instalar o ambiente Data Science+ 1 mais lições
02
NumPy essencial
→ Arrays NumPy vs listas Python→ Operações vetorizadas+ 1 mais lições
03
Pandas Series e DataFrames
→ Criar uma Series→ Criar um DataFrame+ 2 mais lições
04
Leitura e escrita de dados
→ CSV com read_csv e to_csv→ Excel, JSON e Parquet+ 1 mais lições
05
Limpeza de dados
→ Valores ausentes (NaN)→ Duplicatas e inconsistências+ 1 mais lições
06
Agregações e groupby
→ groupby básico→ agg em várias colunas+ 1 mais lições
07
Junções e fusões
→ merge como no SQL→ concat vs merge+ 1 mais lições
08
Visualizações rápidas
→ plot Pandas direto→ Matplotlib para DS+ 1 mais lições
🏁
Projeto final (+ 2 capítulos no caminho)
→ Você sai com um projeto concreto e demonstrável

Duplicatas e inconsistências

NOTEObjetivo — Aprender a identificar e eliminar duplicatas exatas ou aproximadas, e a uniformizar cadeias de caracteres (maiúsculas, espaços, acentos) que envenenam silenciosamente suas análises.

Objetivos pedagógicos

TIPAo final deste módulo — Você saberá detectar linhas duplicadas, decidir qual cópia manter, normalizar colunas de texto e corrigir as famosas inconsistências do tipo « Paris », « paris », « PARIS ».

Por que as duplicatas são perigosas

Uma duplicata é a mesma realidade contada duas vezes. Consequências: faturamento inflado, médias enviesadas, modelos superajustados. Uma duplicata pode vir de uma importação repetida, de um join mal feito, de um formulário enviado duas vezes…

WARNINGHistória real — Uma empresa calculava seu faturamento a partir de um arquivo Excel concatenado todo mês… sem remover as duplicatas. Seu faturamento estava superestimado em 12 %% há dois anos.

Detectar duplicatas

output
import pandas as pd

df = pd.DataFrame({
    "id": [1, 2, 3, 2, 4, 1],
    "nom": ["Alice", "Bob", "Chloe", "Bob", "David", "Alice"],
    "ville": ["Paris", "Lyon", "Nice", "Lyon", "Lille", "Paris"]
})

# Máscara booleana : True se a linha é uma duplicata
df.duplicated()

# Quantas duplicatas no total?
df.duplicated().sum()

# Exibir apenas as duplicatas
df[df.duplicated()]

# Duplicatas baseando-se apenas em algumas colunas
df.duplicated(subset=["id"])
df.duplicated(subset=["nom", "ville"])
NOTEPor padrãoduplicated() marca True a partir da ocorrência. O primeiro exemplar é considerado o original.

Remover duplicatas

output
# Manter a primeira ocorrência (padrão)
df.drop_duplicates()

# Manter a última ocorrência
df.drop_duplicates(keep="last")

# Remover todas as ocorrências duplicadas
df.drop_duplicates(keep=False)

# Em algumas colunas apenas (útil se uma coluna for um timestamp)
df.drop_duplicates(subset=["id"])

# Inplace para modificar o DataFrame diretamente
df.drop_duplicates(inplace=True)
TIPEscolher keep"first" para manter o registro mais antigo, "last" para o mais recente (geralmente a escolha certa se os dados foram atualizados), False para remover tudo e investigar.

A armadilha das duplicatas aproximadas

« Paris » e « paris » são diferentes para o Pandas, mesmo que para você seja a mesma cidade. Antes de procurar duplicatas, normalize.

output
df = pd.DataFrame({
    "email": ["Alice@MAIL.com", " alice@mail.com ", "bob@mail.com"],
    "ville": ["Paris", "PARIS", "Lyon"]
})

# Normalizar os emails (espaços + minúsculas)
df["email"] = df["email"].str.strip().str.lower()
df["ville"] = df["ville"].str.strip().str.title()

# AGORA podemos detectar as verdadeiras duplicatas
df.duplicated(subset=["email"])
print(df)

Caixa de ferramentas .str

O acessor .str dá acesso a todos os métodos de strings Python, vetorizados:

output
s = pd.Series(["  Alice DUPONT  ", "bob martin", "CHLOE.LEROY"])

s.str.strip()                  # remove espaços
s.str.lower()                  # minúsculas
s.str.upper()                  # MAIÚSCULAS
s.str.title()                  # Primeira Letra de Cada Palavra
s.str.replace(".", " ")      # substituição
s.str.contains("alice", case=False)  # filtro
s.str.startswith("A")
s.str.len()                    # número de caracteres
s.str.split(" ", expand=True)  # separar em colunas
NOTEDica acentos — Para normalizar acentos (é → e), use unicodedata ou a biblioteca unidecode: df["ville"].apply(unidecode).

Inconsistências categóricas

Com frequência se digita a mesma categoria de formas diferentes: « H », « Homme », « M », « Masculin »… O Pandas vê 4 categorias distintas.

output
df = pd.DataFrame({
    "sexe": ["H", "Homme", "M", "Masculin", "F", "Femme", "f"]
})

# Antes: ver os valores únicos
print(df["sexe"].value_counts())

# Estratégia: dicionário de mapeamento
mapping = {
    "H": "Homme", "M": "Homme", "Masculin": "Homme",
    "F": "Femme", "f": "Femme", "Femme": "Femme"
}
df["sexe"] = df["sexe"].map(mapping)

print(df["sexe"].value_counts())
TIPReflexo de ouro — Antes de qualquer análise categórica, execute df["col"].value_counts(). As inconsistências saltam imediatamente aos olhos.

Mini-projeto: limpar uma base de funcionários

output
import pandas as pd

df = pd.DataFrame({
    "id":    [1, 2, 3, 2, 4, 5],
    "nom":   ["Alice", " Bob ", "Chloe", "BOB", "David", "emma"],
    "poste": ["Dev", "DEV", "PM", "dev", "Pm", "Designer"],
    "ville": ["Paris", "paris", "Lyon", "PARIS", "Lille", "Lyon"]
})

# 1. Normalizar as strings
for col in ["nom", "poste", "ville"]:
    df[col] = df[col].str.strip().str.title()

# 2. Detectar duplicatas no id
print("Duplicatas :", df.duplicated(subset=["id"]).sum())

# 3. Manter o último registro (o mais atualizado)
df = df.drop_duplicates(subset=["id"], keep="last")

# 4. Verificar
print(df)
print(df["poste"].value_counts())

Valores ausentes (NaN)

NOTEObjetivo — Compreender o que é um valor ausente no Pandas, como detectá-lo, analisá-lo e escolher a estratégia correta: remover ou imputar (preencher). Nenhuma análise limpa é feita sem esta etapa.

Objetivos pedagógicos

TIPAo final deste módulo — Você saberá contar os NaN por coluna, remover linhas incompletas ou preenchê-las inteligentemente (média, mediana, valor fixo, propagação). Também saberá quando cada estratégia é apropriada.

O que é um NaN?

NaN = « Not a Number ». É a forma como o Pandas (herdado do NumPy) representa um valor ausente em uma coluna numérica. Para datas, é NaT; para objetos genéricos, None. O Pandas trata os três de forma unificada via isnull().

output
import pandas as pd
import numpy as np

df = pd.DataFrame({
    "nom": ["Alice", "Bob", None, "David"],
    "age": [30, np.nan, 25, 35],
    "date": pd.to_datetime(["2025-01-01", None, "2025-01-03", "2025-01-04"])
})
print(df)
WARNINGArmadilha clássicaNaN != NaN em Python puro (é intencional). Portanto x == np.nan é sempre falso. Use sempre isnull() ou isna() para testar.

Detectar valores ausentes

output
# Máscara booleana célula por célula
df.isnull()                # True se NaN
df.notna()                 # inverso

# Contar por coluna
df.isnull().sum()

# Porcentagem de NaN por coluna
(df.isnull().sum() / len(df) * 100).round(2)

# Total em todo o DataFrame
df.isnull().sum().sum()

# Linhas que têm pelo menos um NaN
df[df.isnull().any(axis=1)]
TIPReflexo de ouro — Assim que um dataset chega, execute df.isnull().sum(). Em 2 segundos você tem o painel da qualidade dos dados.

Estratégia 1 — Remover (dropna)

Quando os NaN são raros ou a análise exige dados completos:

output
# Remover toda linha que contém pelo menos 1 NaN
df.dropna()

# Remover apenas se TODOS os valores forem NaN
df.dropna(how="all")

# Remover se NaN em uma coluna específica
df.dropna(subset=["age"])

# Manter uma linha se ela tiver pelo menos 3 valores não-NaN
df.dropna(thresh=3)

# Remover uma coluna inteira muito incompleta
df.dropna(axis=1, thresh=100)   # colunas < 100 valores removidas
WARNINGQuando evitar dropna? — Em um dataset de 100 000 linhas onde cada coluna tem alguns %% de NaN, dropna pode remover tudo! Verifique antes: df.dropna().shape vs df.shape.

Estratégia 2 — Preencher (fillna)

Com um valor fixo

output
df["age"].fillna(0)
df["ville"].fillna("Inconnu")

# Em todo o DataFrame com um dict por coluna
df.fillna({"age": 0, "ville": "Inconnu", "salaire": 2000})

Com uma estatística da coluna

output
df["age"].fillna(df["age"].mean())     # média
df["age"].fillna(df["age"].median())   # mediana (robusta)
df["ville"].fillna(df["ville"].mode()[0])  # valor mais frequente

Por propagação (útil para séries temporais)

output
# Preencher com o valor anterior (forward fill)
df["prix"].ffill()

# Com o valor seguinte (backward fill)
df["prix"].bfill()

# Limitar o número de preenchimentos consecutivos
df["prix"].ffill(limit=3)

Tipos de dados e conversão

NOTEObjetivo — Compreender os tipos do Pandas (int, float, object, datetime, category, bool), por que eles importam enormemente, e como converter corretamente sem travar em valores sujos.

Objetivos pedagógicos

TIPAo final deste módulo — Você saberá inspecionar dtypes, converter em número/data/categoria, usar errors="coerce" para valores sujos e reduzir a pegada de memória de um DataFrame passando objectcategory.

Por que o tipo importa

Um « 42 » armazenado como texto (object) não pode ser somado nem comparado numericamente. Uma data armazenada como texto não permite .dt.year. Tipo ruim = bugs silenciosos ou perda de funcionalidades.

output
import pandas as pd

df = pd.DataFrame({
    "prix": ["12.50", "8.99", "15.00"],
    "date": ["2025-01-01", "2025-02-15", "2025-03-10"]
})

print(df.dtypes)
# prix    object  <-- texto!
# date    object  <-- texto!

df["prix"].sum()   # concatena « 12.508.9915.00 »!
WARNINGArmadilha clássicaobject = « qualquer coisa », na maioria das vezes texto. Se você vir object em uma coluna que deveria ser numérica, há um problema a resolver antes de qualquer análise.

Os tipos Pandas em resumo

DtypeUsoExemplo
int64InteiroIdades, quantidades
float64RealPreços, temperaturas
boolVerdadeiro/FalsoAtivo, pago
objectGenérico (geralmente str)Nomes, emails
datetime64[ns]Data/horaPedido, nascimento
timedelta64DuraçãoDiferença entre 2 datas
categoryCategorias repetidasSexo, cidade, status

Conversão com astype

output
# Conversão simples, falha se um valor não for adequado
df["age"] = df["age"].astype(int)
df["prix"] = df["prix"].astype(float)
df["actif"] = df["actif"].astype(bool)
df["sexe"] = df["sexe"].astype("category")

# Em várias colunas ao mesmo tempo
df = df.astype({"age": int, "prix": float, "ville": "category"})
WARNINGLimiteastype(int) trava assim que um valor não converte (ex: "12 EUR"). Para lidar com esses casos, use pd.to_numeric.

pd.to_numeric — o tolerante

output
s = pd.Series(["12.5", "8.99", "oops", "15"])

# Modo estrito: trava se um valor não for numérico
pd.to_numeric(s)   # ValueError

# Modo tolerante: valores sujos -> NaN
pd.to_numeric(s, errors="coerce")
# 0    12.50
# 1     8.99
# 2      NaN  <-- "oops" vira NaN
# 3    15.00

# Escolher o subtipo para economizar memória
pd.to_numeric(s, errors="coerce", downcast="float")
pd.to_numeric(s, errors="coerce", downcast="integer")
TIPWorkflow limpoerrors="coerce" → conte os NaN gerados → decida o que fazer (imputar, remover, sinalizar).

pd.to_datetime — todas as datas

output
# Reconhece a maioria dos formatos automaticamente
pd.to_datetime(["2025-01-15", "15/01/2025", "Jan 15, 2025"])

# Formato explícito (mais rápido, mais seguro)
pd.to_datetime(df["date"], format="%%d/%%m/%%Y")

# Tolerante a valores sujos
pd.to_datetime(df["date"], errors="coerce")

# Uma vez em datetime, acessamos os componentes
df["date"] = pd.to_datetime(df["date"])
df["annee"]    = df["date"].dt.year
df["mois"]     = df["date"].dt.month
df["jour_sem"] = df["date"].dt.day_name()
NOTEFormato explícito — Em grandes volumes, especificar format= pode multiplicar a velocidade por 100. O Pandas não precisa mais adivinhar.

Tipo category: a mágica de memória

Para uma coluna « ville » repetida 1 milhão de vezes, armazenar cada string é desperdício de espaço. category armazena cada valor único uma vez e referencia um índice inteiro.

output
import pandas as pd

df = pd.DataFrame({
    "ville": ["Paris", "Lyon", "Paris"] * 1_000_000
})

print(df.memory_usage(deep=True).sum() / 1e6, "Mo")
# Aproximadamente 200 Mo

df["ville"] = df["ville"].astype("category")
print(df.memory_usage(deep=True).sum() / 1e6, "Mo")
# Aproximadamente 8 Mo -- ganho x25!
TIPQuando usar category? — Quando o número de valores únicos for pequeno em relação ao número total de linhas (tipicamente < 5%%). Caso contrário, pouco ganho.

Mini-projeto: limpar uma exportação contábil

output
import pandas as pd

df = pd.DataFrame({
    "montant":    ["125,50", "99,00", "N/D", "42,75"],
    "date":       ["01/03/2025", "02/03/2025", "oops", "04/03/2025"],
    "categorie":  ["Achat", "Vente", "Achat", "Vente"],
    "paye":       ["Oui", "Non", "Oui", "Oui"]
})

# 1. Montant: vírgula -> ponto, depois numérico
df["montant"] = df["montant"].str.replace(",", ".")
df["montant"] = pd.to_numeric(df["montant"], errors="coerce")

# 2. Data no formato dd/mm/aaaa
df["date"] = pd.to_datetime(df["date"], format="%%d/%%m/%%Y", errors="coerce")

# 3. Categorie -> tipo category
df["categorie"] = df["categorie"].astype("category")

# 4. Oui/Non -> bool
df["paye"] = df["paye"].map({"Oui": True, "Non": False})

print(df.dtypes)
print(df)
va-plus-loin

Este artigo cobre os trechos mais úteis — o curso completo Python Data Science (11 capítulos, 36 lições, exercícios corrigidos e projeto final) leva você até o fim.

./acceder-au-cours-complet curso gratuito: Dominando o Claude Code

FAQ

Quanto tempo para aprender Python Data Science?
Com uma progressão estruturada (11 capítulos, 36 lições curtas e práticas), alcança-se um nível operacional em algumas semanas dedicando 30 a 60 minutos por dia. O importante é praticar cada conceito imediatamente.
É preciso ter pré-requisitos?
Básicos de informática são suficientes. Se você sabe usar um terminal e ler código simples, está pronto.
Por onde começar concretamente?
Reproduza os comandos deste artigo e depois siga o curso completo Python Data Science: ele encadeia as 36 lições na ordem, com exercícios e projeto final.

📬 Quer receber este tipo de guia toda semana? Inscreva-se gratuitamente — código real, zero enrolação.