Objetivo
Dominar o Pandas, a biblioteca padrão para manipulação de dados tabulares em Python. Aprender a criar, carregar, inspecionar e operar sobre Series e DataFrames, que são as estruturas de dados fundamentais para qualquer projeto de Machine Learning.
1. Por que Pandas é Essencial em IA?
Em todo projeto de ML, antes de qualquer modelo, você precisa lidar com dados. E dados reais vêm em formatos tabulares: CSVs, planilhas Excel, bancos de dados relacionais, resultados de APIs em JSON. O Pandas é a ferramenta que transforma esses dados brutos em algo que você pode analisar, limpar e usar para treinar modelos.
Na prática:
- Carregar um CSV com 1 milhão de linhas: uma linha com Pandas
- Calcular estatísticas por grupo em segundos:
.groupby() - Tratar valores ausentes em todo o dataset de uma vez:
.fillna() - Converter um DataFrame em arrays NumPy para passar ao modelo:
.values - Inspecionar o dataset antes de treinar:
.describe(),.info(),.head()
O fluxo padrão de um projeto de ML é: dados brutos → Pandas (limpeza e análise) → NumPy/PyTorch (modelagem).
2. Importando Pandas
A convenção universal é importar com o alias pd:
import pandas as pd
import numpy as np
print(pd.__version__)
3. Series
Uma Series é um array unidimensional com rótulos (índice). É como uma coluna de uma tabela, ou um dicionário ordenado.
3.1 Criando Series
# A partir de uma lista
notas = pd.Series([7.5, 8.0, 6.5, 9.0, 5.5])
print(notas)
Saída:
0 7.5
1 8.0
2 6.5
3 9.0
4 5.5
dtype: float64
# Com índice personalizado
notas = pd.Series(
[7.5, 8.0, 6.5, 9.0, 5.5],
index=["Ana", "Bruno", "Carla", "Diego", "Eva"]
)
print(notas)
Saída:
Ana 7.5
Bruno 8.0
Carla 6.5
Diego 9.0
Eva 5.5
dtype: float64
# A partir de um dicionário
populacao = pd.Series({
"São Paulo": 12_325_232,
"Rio de Janeiro": 6_747_815,
"Brasília": 3_094_325,
"Salvador": 2_886_698
})
print(populacao)
3.2 Acessando elementos
notas = pd.Series(
[7.5, 8.0, 6.5, 9.0, 5.5],
index=["Ana", "Bruno", "Carla", "Diego", "Eva"]
)
# Por rótulo (loc)
print(notas["Ana"]) # 7.5
print(notas.loc["Bruno"]) # 8.0
# Por posição (iloc)
print(notas.iloc[0]) # 7.5
print(notas.iloc[-1]) # 5.5
print(notas.iloc[1:3]) # Bruno e Carla
# Múltiplos rótulos
print(notas[["Ana", "Diego"]])
3.3 Operações com Series
notas = pd.Series([7.5, 8.0, 6.5, 9.0, 5.5])
# Operações escalares
print(notas + 1)
print(notas * 2)
print(notas > 7)
# Estatísticas
print(notas.mean()) # 7.3
print(notas.std()) # 1.3...
print(notas.min()) # 5.5
print(notas.max()) # 9.0
print(notas.median()) # 7.5
print(notas.sum()) # 36.5
# Describe: resumo estatístico
print(notas.describe())
Saída de describe():
count 5.000000
mean 7.300000
std 1.303840
min 5.500000
25% 6.500000
50% 7.500000
75% 8.000000
max 9.000000
dtype: float64
3.4 Valores ausentes em Series
s = pd.Series([1.0, None, 3.0, None, 5.0])
print(s)
print(s.isna()) # máscara booleana de NaN
print(s.isna().sum()) # 2 — quantos NaN
print(s.dropna()) # remove NaN
print(s.fillna(0)) # substitui NaN por 0
print(s.fillna(s.mean())) # substitui NaN pela média
4. DataFrames
Um DataFrame é uma tabela bidimensional com rótulos nas linhas (índice) e colunas. É a estrutura mais importante do Pandas — pense em uma planilha Excel programável.
4.1 Criando DataFrames
# A partir de um dicionário de listas
df = pd.DataFrame({
"nome": ["Ana", "Bruno", "Carla", "Diego", "Eva"],
"idade": [25, 30, 28, 35, 22],
"nota": [8.5, 7.0, 9.2, 6.5, 8.0],
"cidade": ["SP", "RJ", "SP", "MG", "SP"]
})
print(df)
Saída:
nome idade nota cidade
0 Ana 25 8.5 SP
1 Bruno 30 7.0 RJ
2 Carla 28 9.2 SP
3 Diego 35 6.5 MG
4 Eva 22 8.0 SP
# A partir de lista de dicionários
registros = [
{"produto": "notebook", "preco": 3500, "estoque": 10},
{"produto": "mouse", "preco": 90, "estoque": 50},
{"produto": "teclado", "preco": 150, "estoque": 30},
]
df_produtos = pd.DataFrame(registros)
# A partir de array NumPy
arr = np.random.randn(4, 3)
df_numpy = pd.DataFrame(arr, columns=["feature_1", "feature_2", "feature_3"])
print(df_numpy)
4.2 Carregando dados de arquivos
# CSV — o mais comum
df = pd.read_csv("dados.csv")
df = pd.read_csv("dados.csv", sep=";") # separador diferente
df = pd.read_csv("dados.csv", encoding="utf-8") # encoding
df = pd.read_csv("dados.csv", index_col="id") # definir coluna como índice
df = pd.read_csv("dados.csv", nrows=1000) # apenas 1000 linhas
df = pd.read_csv("dados.csv", usecols=["a", "b"]) # apenas algumas colunas
# Excel
df = pd.read_excel("dados.xlsx", sheet_name="Plan1")
# JSON
df = pd.read_json("dados.json")
# Salvando
df.to_csv("saida.csv", index=False) # index=False evita salvar o índice
df.to_excel("saida.xlsx", index=False)
df.to_json("saida.json", orient="records")
5. Inspeção Inicial do DataFrame
Estas são as primeiras coisas que você faz ao carregar qualquer dataset:
df = pd.DataFrame({
"nome": ["Ana", "Bruno", "Carla", "Diego", "Eva", "Fábio"],
"idade": [25, 30, 28, 35, 22, None],
"nota": [8.5, 7.0, 9.2, 6.5, 8.0, 7.5],
"cidade": ["SP", "RJ", "SP", "MG", "SP", "RJ"],
"aprovado": [True, True, True, False, True, True]
})
# Primeiras e últimas linhas
print(df.head()) # primeiras 5 linhas (padrão)
print(df.head(3)) # primeiras 3 linhas
print(df.tail()) # últimas 5 linhas
# Dimensões
print(df.shape) # (6, 5) — linhas, colunas
# Informações gerais
print(df.info())
Saída de info():
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 nome 6 non-null object
1 idade 5 non-null float64
2 nota 6 non-null float64
3 cidade 6 non-null object
4 aprovado 6 non-null bool
dtypes: bool(1), float64(2), object(2)
memory usage: 370.0+ bytes
# Estatísticas descritivas
print(df.describe())
# Incluindo colunas não numéricas
print(df.describe(include="all"))
# Tipos de dados
print(df.dtypes)
# Nomes das colunas
print(df.columns.tolist())
# Índice
print(df.index)
# Valores únicos por coluna
print(df["cidade"].unique()) # ['SP' 'RJ' 'MG']
print(df["cidade"].nunique()) # 3 — quantidade de únicos
print(df["cidade"].value_counts()) # frequência de cada valor
6. Selecionando Dados
6.1 Selecionando colunas
# Uma coluna → retorna Series
nomes = df["nome"]
print(type(nomes)) # <class 'pandas.core.series.Series'>
# Múltiplas colunas → retorna DataFrame
subdf = df[["nome", "nota"]]
print(type(subdf)) # <class 'pandas.core.frame.DataFrame'>
6.2 loc — seleção por rótulo
# Uma linha por índice
print(df.loc[0])
# Múltiplas linhas
print(df.loc[0:2]) # linhas 0, 1 e 2 (inclusive)
# Linha e colunas específicas
print(df.loc[0, "nome"]) # "Ana"
print(df.loc[0:2, ["nome", "nota"]])
# Todas as linhas, colunas específicas
print(df.loc[:, "nota"])
6.3 iloc — seleção por posição
# Por índice numérico
print(df.iloc[0]) # primeira linha
print(df.iloc[-1]) # última linha
print(df.iloc[0:3]) # primeiras 3 linhas
print(df.iloc[0, 1]) # linha 0, coluna 1
print(df.iloc[:, 2]) # toda a coluna 2
print(df.iloc[0:3, 0:2]) # submatriz
6.4 Filtragem booleana
# Filtro simples
aprovados = df[df["aprovado"] == True]
print(aprovados)
# Equivalente Pythonico
aprovados = df[df["aprovado"]]
# Notas altas
notas_altas = df[df["nota"] >= 8.0]
print(notas_altas)
# Múltiplas condições com & (e) e | (ou)
sp_aprovados = df[(df["cidade"] == "SP") & (df["aprovado"] == True)]
print(sp_aprovados)
jovens_ou_notas_altas = df[(df["idade"] < 25) | (df["nota"] > 9.0)]
print(jovens_ou_notas_altas)
# Usando query() — sintaxe mais legível
resultado = df.query("nota >= 8 and cidade == 'SP'")
print(resultado)
# isin — verificar pertencimento a uma lista
sudeste = df[df["cidade"].isin(["SP", "RJ", "MG"])]
print(sudeste)
7. Manipulando Colunas
7.1 Adicionando colunas
df = pd.DataFrame({
"nome": ["Ana", "Bruno", "Carla"],
"nota1": [8.0, 7.0, 9.0],
"nota2": [7.5, 8.5, 8.0],
})
# Nova coluna com operação
df["media"] = (df["nota1"] + df["nota2"]) / 2
# Nova coluna com condição
df["situacao"] = df["media"].apply(lambda x: "Aprovado" if x >= 7 else "Reprovado")
# Com np.where — mais eficiente para operações simples
df["situacao2"] = np.where(df["media"] >= 7, "Aprovado", "Reprovado")
print(df)
Saída:
nome nota1 nota2 media situacao situacao2
0 Ana 8.0 7.5 7.75 Aprovado Aprovado
1 Bruno 7.0 8.5 7.75 Aprovado Aprovado
2 Carla 9.0 8.0 8.50 Aprovado Aprovado
7.2 Removendo colunas e linhas
# Remover colunas
df_sem_nota2 = df.drop(columns=["nota2"])
df_sem_notas = df.drop(columns=["nota1", "nota2"])
# Remover linhas por índice
df_sem_primeira = df.drop(index=0)
df_sem_algumas = df.drop(index=[0, 2])
# inplace=True modifica o DataFrame original
df.drop(columns=["situacao2"], inplace=True)
7.3 Renomeando colunas
df = df.rename(columns={"nota1": "prova1", "nota2": "prova2"})
print(df.columns.tolist())
7.4 Alterando tipos
df["nota1"] = df["nota1"].astype(np.float32)
df["nome"] = df["nome"].astype("category") # eficiente para strings repetidas
8. Valores Ausentes
Lidar com valores ausentes é uma das tarefas mais comuns em ciência de dados.
df = pd.DataFrame({
"nome": ["Ana", "Bruno", "Carla", "Diego", "Eva"],
"idade": [25, None, 28, 35, None],
"salario": [3000, 4500, None, 5000, 3500],
"cidade": ["SP", "RJ", None, "MG", "SP"]
})
# Verificar ausentes
print(df.isna())
print(df.isna().sum()) # por coluna
print(df.isna().sum().sum()) # total
# Porcentagem de ausentes
print(df.isna().mean() * 100)
# Remover linhas com qualquer ausente
df_limpo = df.dropna()
# Remover linhas onde colunas específicas têm ausentes
df_sem_idade_nula = df.dropna(subset=["idade"])
# Preencher com valor fixo
df_filled = df.fillna(0)
# Preencher por coluna com estratégias diferentes
df["idade"] = df["idade"].fillna(df["idade"].mean())
df["cidade"] = df["cidade"].fillna("Desconhecido")
df["salario"] = df["salario"].fillna(df["salario"].median())
print(df)
9. Operações de String
Pandas tem um acessor .str para operações vetorizadas em colunas de texto:
df = pd.DataFrame({
"nome": [" ana silva ", "BRUNO COSTA", "carla SANTOS"],
"email": ["ana@email.com", "bruno@gmail.com", "carla@yahoo.com"]
})
# Limpeza e normalização
df["nome_limpo"] = (df["nome"]
.str.strip() # remove espaços nas bordas
.str.title() # capitaliza cada palavra
)
# Verificações
df["tem_gmail"] = df["email"].str.contains("gmail")
df["dominio"] = df["email"].str.split("@").str[1]
df["nome_upper"] = df["nome"].str.upper()
# Substituição
df["email_limpo"] = df["email"].str.replace("@yahoo.com", "@email.com")
print(df[["nome_limpo", "dominio", "tem_gmail"]])
Saída:
nome_limpo dominio tem_gmail
0 Ana Silva email.com False
1 Bruno Costa gmail.com True
2 Carla Santos yahoo.com False
10. Ordenação
df = pd.DataFrame({
"nome": ["Ana", "Bruno", "Carla", "Diego"],
"nota": [8.5, 7.0, 9.2, 6.5],
"idade": [25, 30, 25, 35]
})
# Por uma coluna
print(df.sort_values("nota"))
print(df.sort_values("nota", ascending=False))
# Por múltiplas colunas
print(df.sort_values(["idade", "nota"], ascending=[True, False]))
# Por índice
print(df.sort_index())
11. Convertendo para NumPy
Em projetos de ML, o fluxo final é sempre converter o DataFrame em arrays NumPy para alimentar o modelo:
df = pd.DataFrame({
"feature1": [1.0, 2.0, 3.0, 4.0],
"feature2": [0.5, 1.5, 2.5, 3.5],
"label": [0, 1, 0, 1]
})
# Separar features e labels
X = df[["feature1", "feature2"]].values # array NumPy (4, 2)
y = df["label"].values # array NumPy (4,)
print(type(X)) # <class 'numpy.ndarray'>
print(X.shape) # (4, 2)
print(y.shape) # (4,)
print(X.dtype) # float64
# Garantir tipo float32 para PyTorch
X = X.astype(np.float32)
y = y.astype(np.int64)
12. Exemplo Completo: Análise de Dataset de ML
import pandas as pd
import numpy as np
np.random.seed(42)
# Simulando um dataset de clientes para modelo de churn
n = 200
df = pd.DataFrame({
"cliente_id": range(1, n + 1),
"idade": np.random.randint(18, 70, n),
"tempo_cliente": np.random.randint(1, 120, n), # meses
"gasto_mensal": np.random.uniform(50, 500, n).round(2),
"num_produtos": np.random.randint(1, 6, n),
"satisfacao": np.random.choice([1, 2, 3, 4, 5], n),
"churn": np.random.choice([0, 1], n, p=[0.75, 0.25])
})
# Inserir alguns valores ausentes artificialmente
idx_ausentes = np.random.choice(n, 15, replace=False)
df.loc[idx_ausentes[:8], "gasto_mensal"] = np.nan
df.loc[idx_ausentes[8:], "satisfacao"] = np.nan
print("=" * 50)
print("1. INSPEÇÃO INICIAL")
print("=" * 50)
print(f"Shape: {df.shape}")
print(f"\nPrimeiras linhas:")
print(df.head())
print(f"\nTipos:")
print(df.dtypes)
print("\n" + "=" * 50)
print("2. QUALIDADE DOS DADOS")
print("=" * 50)
ausentes = df.isna().sum()
ausentes = ausentes[ausentes > 0]
print(f"Valores ausentes:\n{ausentes}")
print(f"\nPorcentagem ausente:")
print((ausentes / len(df) * 100).round(2))
print("\n" + "=" * 50)
print("3. ESTATÍSTICAS DESCRITIVAS")
print("=" * 50)
print(df.describe().round(2))
print("\n" + "=" * 50)
print("4. DISTRIBUIÇÃO DO TARGET")
print("=" * 50)
contagem = df["churn"].value_counts()
pct = df["churn"].value_counts(normalize=True) * 100
print(pd.DataFrame({"contagem": contagem, "porcentagem": pct.round(1)}))
print("\n" + "=" * 50)
print("5. ANÁLISE POR GRUPO")
print("=" * 50)
analise = df.groupby("churn")[["idade", "gasto_mensal",
"tempo_cliente", "satisfacao"]].mean().round(2)
print(analise)
print("\n" + "=" * 50)
print("6. TRATAMENTO DE AUSENTES")
print("=" * 50)
df["gasto_mensal"] = df["gasto_mensal"].fillna(df["gasto_mensal"].median())
df["satisfacao"] = df["satisfacao"].fillna(df["satisfacao"].mode()[0])
print(f"Ausentes após tratamento: {df.isna().sum().sum()}")
print("\n" + "=" * 50)
print("7. PREPARANDO PARA O MODELO")
print("=" * 50)
features = ["idade", "tempo_cliente", "gasto_mensal",
"num_produtos", "satisfacao"]
X = df[features].values.astype(np.float32)
y = df["churn"].values.astype(np.int64)
print(f"X shape: {X.shape}")
print(f"y shape: {y.shape}")
print(f"X dtype: {X.dtype}")
print(f"y dtype: {y.dtype}")
# Divisão treino/teste
corte = int(len(df) * 0.8)
X_train, X_test = X[:corte], X[corte:]
y_train, y_test = y[:corte], y[corte:]
print(f"\nTreino: {X_train.shape[0]} exemplos")
print(f"Teste: {X_test.shape[0]} exemplos")
Saída resumida:
==================================================
1. INSPEÇÃO INICIAL
==================================================
Shape: (200, 8)
Primeiras linhas:
cliente_id idade tempo_cliente gasto_mensal ... churn
0 1 57 65 446.22 ... 0
1 2 37 64 146.04 ... 0
...
==================================================
4. DISTRIBUIÇÃO DO TARGET
==================================================
contagem porcentagem
churn
0 151 75.5
1 49 24.5
==================================================
5. ANÁLISE POR GRUPO
==================================================
idade gasto_mensal tempo_cliente satisfacao
churn
0 43.12 275.43 59.82 3.11
1 44.02 271.89 57.94 2.94
==================================================
7. PREPARANDO PARA O MODELO
==================================================
X shape: (200, 5)
y shape: (200,)
X dtype: float32
y dtype: int64
Treino: 160 exemplos
Teste: 40 exemplos
Resumo da Aula
- Series é um array 1D com índice: pense em uma coluna de tabela
- DataFrame é uma tabela 2D com índice e colunas: pense em uma planilha
read_csv(),read_excel(),read_json()carregam dados de arquivoshead(),info(),describe()são os primeiros comandos ao explorar um datasetlocseleciona por rótulo,ilocseleciona por posição numérica- Filtragem booleana:
df[df["coluna"] > valor]— use&e|para múltiplas condições isna(),fillna(),dropna()tratam valores ausentes.strpermite operações vetorizadas em colunas de textogroupby()agrega dados por grupo (próxima aula).valuesconverte DataFrame/Series para array NumPy- O fluxo final sempre é: DataFrame limpo → NumPy → modelo
Exercícios
-
Crie um DataFrame com dados de 5 funcionários contendo: nome, departamento, salário e anos de empresa. Em seguida: exiba apenas os funcionários do departamento "TI", ordene por salário decrescente e adicione uma coluna "bonus" que seja 10% do salário para quem tem mais de 3 anos de empresa e 5% para os demais.
✓ Resposta:df = pd.DataFrame({ "nome": ["Ana", "Bruno", "Carla", "Diego", "Eva"], "departamento": ["TI", "RH", "TI", "TI", "Financeiro"], "salario": [8000, 5000, 9500, 7000, 6000], "anos": [5, 2, 8, 1, 4] }) # Filtrar TI ti = df[df["departamento"] == "TI"].copy() # Ordenar por salário ti = ti.sort_values("salario", ascending=False) # Adicionar bônus ti["bonus"] = ti.apply( lambda row: row["salario"] * 0.10 if row["anos"] > 3 else row["salario"] * 0.05, axis=1 ) print(ti)Saída:
nome departamento salario anos bonus 2 Carla TI 9500 8 950.0 0 Ana TI 8000 5 800.0 3 Diego TI 7000 1 350.0 -
Dado o DataFrame abaixo com valores ausentes, escreva o código para: identificar quantos ausentes há em cada coluna, preencher os ausentes numéricos com a mediana da coluna e os ausentes de texto com "Desconhecido".
df = pd.DataFrame({ "produto": ["A", "B", None, "D", "E"], "preco": [10.0, None, 30.0, None, 50.0], "estoque": [100, 200, None, 400, 500], "categoria": ["X", "Y", "X", None, "Y"] })✓ Resposta:df = pd.DataFrame({ "produto": ["A", "B", None, "D", "E"], "preco": [10.0, None, 30.0, None, 50.0], "estoque": [100, 200, None, 400, 500], "categoria": ["X", "Y", "X", None, "Y"] }) # Identificar ausentes print("Ausentes por coluna:") print(df.isna().sum()) # Colunas numéricas — preencher com mediana colunas_numericas = df.select_dtypes(include=[np.number]).columns for col in colunas_numericas: df[col] = df[col].fillna(df[col].median()) # Colunas de texto — preencher com "Desconhecido" colunas_texto = df.select_dtypes(include=["object"]).columns for col in colunas_texto: df[col] = df[col].fillna("Desconhecido") print("\nApós tratamento:") print(df) print(f"\nAusentes restantes: {df.isna().sum().sum()}") -
Explique a diferença entre
df.locedf.iloc. Quando o uso de um pode gerar resultados inesperados se você usar o outro por engano? Dê um exemplo concreto.✓ Resposta:df.locseleciona por rótulo do índice.df.ilocseleciona por posição inteira (0, 1, 2...).Quando o índice do DataFrame é o padrão
RangeIndex(0, 1, 2...), os dois parecem equivalentes, mas têm uma diferença sutil:loccom slicing é inclusivo no final (df.loc[0:2]retorna linhas 0, 1 e 2), enquantoilocé exclusivo no final (df.iloc[0:2]retorna apenas linhas 0 e 1).O problema inesperado surge quando o índice não é sequencial. Por exemplo, após filtrar um DataFrame, o índice original é preservado:
df = pd.DataFrame({ "nome": ["Ana", "Bruno", "Carla", "Diego"], "nota": [8.5, 7.0, 9.2, 6.5] }) # Filtrar apenas aprovados (nota >= 7) aprovados = df[df["nota"] >= 7.0] print(aprovados.index.tolist()) # [0, 1, 2] — índice 3 foi removido # iloc[1] retorna a segunda linha DO RESULTADO FILTRADO (Bruno) print(aprovados.iloc[1]) # Bruno, nota 7.0 # loc[1] retorna a linha com RÓTULO 1 (também Bruno nesse caso) print(aprovados.loc[1]) # Bruno, nota 7.0 # Mas se o índice fosse [0, 2, 5]: # loc[1] → KeyError (não existe rótulo 1) # iloc[1] → segunda posição do resultado (funciona normalmente)Regra prática: após filtrar um DataFrame, use
.reset_index(drop=True)para redefinir o índice se for usarloccom números. -
Dado um DataFrame de notas com colunas
["aluno", "matematica", "portugues", "ciencias"], escreva em uma linha com Pandas: calcule a média das três disciplinas para cada aluno e adicione como coluna "media", depois filtre apenas os alunos com média acima de 7.0.✓ Resposta:df = pd.DataFrame({ "aluno": ["Ana", "Bruno", "Carla", "Diego", "Eva"], "matematica": [8.0, 6.5, 9.0, 5.5, 7.5], "portugues": [7.5, 7.0, 8.5, 6.0, 8.0], "ciencias": [9.0, 5.5, 9.5, 7.0, 6.5] }) # Tudo em uma expressão encadeada resultado = (df.assign(media=df[["matematica", "portugues", "ciencias"]].mean(axis=1)) .query("media > 7.0")) print(resultado)Saída:
aluno matematica portugues ciencias media 0 Ana 8.0 7.5 9.0 8.1667 2 Carla 9.0 8.5 9.5 9.0000 4 Eva 7.5 8.0 6.5 7.3333 -
Explique o que
value_counts()evalue_counts(normalize=True)fazem. Em que situação de ML essa função é especialmente útil?✓ Resposta:value_counts()conta quantas vezes cada valor único aparece em uma Series, retornando em ordem decrescente de frequência.value_counts(normalize=True)faz o mesmo, mas retorna a proporção (fração) em vez de contagem absoluta — os valores somam 1.0.s = pd.Series(["gato", "cachorro", "gato", "peixe", "gato", "cachorro"]) print(s.value_counts()) # gato 3 # cachorro 2 # peixe 1 print(s.value_counts(normalize=True)) # gato 0.500 # cachorro 0.333 # peixe 0.167Em ML essa função é especialmente útil para verificar o balanceamento de classes no dataset antes de treinar um modelo de classificação. Se uma classe aparece em 95% dos exemplos e a outra em apenas 5%, o modelo pode aprender a sempre prever a classe majoritária e ainda assim ter 95% de acurácia — o que é enganoso. Detectar esse desbalanceamento com
value_counts()é o primeiro passo para decidir se você precisa de oversampling, undersampling ou pesos de classe no modelo. -
Por que o método
.valuesé necessário ao preparar dados para um modelo de ML com PyTorch ou Scikit-learn? O que aconteceria se você passasse um DataFrame diretamente para um modelo sem convertê-lo?✓ Resposta:.valuesé necessário porque Scikit-learn e PyTorch esperam arrays NumPy ou tensores como entrada, não DataFrames do Pandas.Um DataFrame carrega informações extras além dos dados em si: o índice, os nomes das colunas, os tipos por coluna, metadados. Embora muitas versões recentes do Scikit-learn aceitem DataFrames diretamente, o PyTorch não aceita — ele precisa de tensores, que são construídos a partir de arrays NumPy.
Além disso, mesmo quando o Scikit-learn aceita DataFrames, é boa prática converter explicitamente para garantir que:
- O tipo de dado é o esperado (float32 para PyTorch, float64 para Scikit-learn)
- Não há índices estranhos que possam causar comportamentos inesperados
- O código é explícito sobre o que está sendo passado ao modelo
Se você passasse um DataFrame diretamente ao PyTorch:
import torch df_features = pd.DataFrame({"a": [1.0, 2.0], "b": [3.0, 4.0]}) # Isso vai dar erro tensor = torch.tensor(df_features) # TypeError # Isso funciona tensor = torch.tensor(df_features.values) # Ou mais explicitamente: tensor = torch.tensor(df_features.values.astype(np.float32))