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.
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.
- Introdução e Instalação
- NumPy essencial
- Pandas Series e DataFrames
- Leitura e escrita de dados
- Limpeza de dados
Duplicatas e inconsistências
Objetivos pedagógicos
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…
Detectar duplicatas
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"])
duplicated() marca True a partir da 2ª ocorrência. O primeiro exemplar é considerado o original.Remover duplicatas
# 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)
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.
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:
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
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.
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())df["col"].value_counts(). As inconsistências saltam imediatamente aos olhos.Mini-projeto: limpar uma base de funcionários
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)
Objetivos pedagógicos
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().
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)
NaN != NaN em Python puro (é intencional). Portanto x == np.nan é sempre falso. Use sempre isnull() ou isna() para testar.Detectar valores ausentes
# 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)]
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:
# 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
df.dropna().shape vs df.shape.Estratégia 2 — Preencher (fillna)
Com um valor fixo
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
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)
# 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
Objetivos pedagógicos
dtypes, converter em número/data/categoria, usar errors="coerce" para valores sujos e reduzir a pegada de memória de um DataFrame passando object → category.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.
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 »!
object = « 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
| Dtype | Uso | Exemplo |
|---|---|---|
int64 | Inteiro | Idades, quantidades |
float64 | Real | Preços, temperaturas |
bool | Verdadeiro/Falso | Ativo, pago |
object | Genérico (geralmente str) | Nomes, emails |
datetime64[ns] | Data/hora | Pedido, nascimento |
timedelta64 | Duração | Diferença entre 2 datas |
category | Categorias repetidas | Sexo, cidade, status |
Conversão com astype
# 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"})
astype(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
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")
errors="coerce" → conte os NaN gerados → decida o que fazer (imputar, remover, sinalizar).pd.to_datetime — todas as datas
# 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()
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.
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!
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
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)
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 CodeFAQ
Quanto tempo para aprender Python Data Science?
É preciso ter pré-requisitos?
Por onde começar concretamente?
📬 Quer receber este tipo de guia toda semana? Inscreva-se gratuitamente — código real, zero enrolação.