Fundamentos do Machine Learning com Python
Exploração e Pré-processamento de Dados no Projeto (Pandas, NumPy, Scikit-learn)
Aprenda sobre exploração e pré-processamento de dados no projeto (pandas, numpy, scikit-learn)
Exploração e Pré-processamento de Dados no Projeto
Olá, futuro especialista em Machine Learning! 👋 Nesta aula fundamental do nosso curso, mergulharemos em uma das etapas mais críticas de qualquer projeto de ML: a Exploração e o Pré-processamento de Dados. Dados brutos raramente estão prontos para serem alimentados em um modelo. Eles precisam ser entendidos, limpos e transformados.
Vamos desmistificar este processo usando as bibliotecas mais poderosas do ecossistema Python: Pandas para manipulação e análise, NumPy para operações numéricas eficientes, e Scikit-learn para transformações e divisão de dados.
Ao final desta aula, você estará apto a:
- Carregar e inspecionar dados de diversas fontes.
- Identificar e tratar valores ausentes e duplicatas.
- Codificar variáveis categóricas para uso em modelos.
- Escalonar variáveis numéricas para otimizar o desempenho do modelo.
- Dividir seu conjunto de dados em subconjuntos de treino e teste.
Vamos começar nossa jornada de preparação de dados! 🚀
1. Carregamento e Exploração Inicial de Dados com Pandas
A primeira etapa em qualquer projeto de ML é carregar seus dados e ter uma visão geral do que você tem em mãos. O Pandas é a ferramenta de eleição para isso, oferecendo DataFrames que são estruturas de dados tabulares flexíveis e eficientes.
1.1. Carregando Dados
Normalmente, seus dados virão em formatos como CSV, Excel, JSON ou bancos de dados. O Pandas torna o carregamento incrivelmente simples.
import pandas as pd
import numpy as np # Usaremos NumPy para algumas operações numéricas e para representar NaN
# Vamos criar um DataFrame de exemplo para esta aula
# Em um projeto real, você usaria pd.read_csv(), pd.read_excel(), etc.
data = {
'Idade': [25, 30, np.nan, 40, 22, 30, 55, np.nan, 28, 35],
'Salario': [50000, 60000, 75000, np.nan, 45000, 60000, 90000, 52000, 58000, 70000],
'Cidade': ['São Paulo', 'Rio de Janeiro', 'Belo Horizonte', 'São Paulo', 'Curitiba', 'Rio de Janeiro', 'São Paulo', 'Porto Alegre', 'Curitiba', 'Belo Horizonte'],
'Experiencia_Anos': [3, 7, 10, 15, 1, 7, 20, 4, 2, 12],
'Genero': ['M', 'F', 'M', 'F', 'F', 'M', 'M', 'F', 'M', 'F'],
'Target': [0, 1, 0, 1, 0, 1, 1, 0, 0, 1] # Variável target de exemplo
}
df = pd.DataFrame(data)
print("DataFrame Original:")
print(df)1.2. Exploração Rápida do DataFrame
Após carregar, é essencial inspecionar o DataFrame para entender sua estrutura, tipos de dados e a presença de valores ausentes.
print("\n--- df.head() ---")
# Exibe as primeiras 5 linhas do DataFrame
print(df.head())
print("\n--- df.info() ---")
# Fornece um resumo conciso do DataFrame, incluindo tipos de dados e valores não nulos
df.info()
print("\n--- df.describe() ---")
# Gera estatísticas descritivas das colunas numéricas
print(df.describe())
print("\n--- df.shape ---")
# Retorna uma tupla representando as dimensões do DataFrame (linhas, colunas)
print(f"Dimensões do DataFrame: {df.shape}")
print("\n--- df.columns ---")
# Lista os nomes das colunas
print(f"Nomes das colunas: {df.columns.tolist()}")
print("\n--- Verificando valores ausentes ---")
# Conta o número de valores ausentes por coluna
print(df.isnull().sum())
print("\n--- Verificando tipos de dados ---")
# Exibe os tipos de dados de cada coluna
print(df.dtypes)Insights da Exploração:
df.head()nos dá uma ideia dos dados.df.info()nos mostra queIdadeeSalariotêm valores nulos (menos de 10 entradas não nulas de 10).df.describe()fornece estatísticas como média, desvio padrão, min/max para colunas numéricas.df.isnull().sum()confirma a presença de 2 valores ausentes emIdadee 1 emSalario.df.dtypesmostra que todas as colunas estão com tipos de dados adequados por enquanto.
2. Limpeza de Dados: Lidando com Imperfeições 🧹
Dados do mundo real são bagunçados. Valores ausentes, duplicatas e tipos de dados inconsistentes são problemas comuns que precisam ser resolvidos antes de treinar um modelo.
2.1. Tratamento de Valores Ausentes (Missing Values)
Existem várias estratégias para lidar com valores ausentes:
- Remoção: Excluir linhas ou colunas com valores ausentes. Use com cautela para não perder dados importantes.
- Imputação: Preencher os valores ausentes com uma estimativa (média, mediana, moda, um valor constante ou até mesmo um modelo de ML).
2.1.1. Remoção de Linhas com Valores Ausentes
# Criar uma cópia para demonstração
df_dropped = df.copy()
# Remove todas as linhas que contêm pelo menos um valor NaN
# Documentação: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.dropna.html
df_dropped.dropna(inplace=True) # inplace=True modifica o DataFrame diretamente
print("\n--- DataFrame após remoção de linhas com NaN ---")
print(df_dropped)
print(f"Dimensões após dropna: {df_dropped.shape}")⚠️ Cuidado: Remover linhas pode reduzir drasticamente o tamanho do seu dataset, especialmente se houver muitos valores ausentes distribuídos.
2.1.2. Imputação de Valores Ausentes
A imputação é geralmente preferível à remoção, pois mantém mais dados.
-
Com Pandas
fillna():df_filled_pandas = df.copy() # Preencher 'Idade' com a mediana (menos sensível a outliers que a média) # Documentação: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.fillna.html median_idade = df_filled_pandas['Idade'].median() df_filled_pandas['Idade'].fillna(median_idade, inplace=True) # Preencher 'Salario' com a média mean_salario = df_filled_pandas['Salario'].mean() df_filled_pandas['Salario'].fillna(mean_salario, inplace=True) print("\n--- DataFrame após imputação com Pandas (mediana/média) ---") print(df_filled_pandas) print(df_filled_pandas.isnull().sum()) -
Com Scikit-learn
SimpleImputer(Melhor prática para pipelines de ML): OSimpleImputeré recomendado porque ele pode ser integrado a pipelines de pré-processamento, garantindo que a imputação seja feita de forma consistente tanto no conjunto de treino quanto no de teste.from sklearn.impute import SimpleImputer df_filled_sklearn = df.copy() # Imputar 'Idade' com a mediana imputer_idade = SimpleImputer(strategy='median') # O fit_transform espera um array 2D, por isso reshape(-1, 1) df_filled_sklearn['Idade'] = imputer_idade.fit_transform(df_filled_sklearn[['Idade']]) # Imputar 'Salario' com a média imputer_salario = SimpleImputer(strategy='mean') df_filled_sklearn['Salario'] = imputer_salario.fit_transform(df_filled_sklearn[['Salario']]) print("\n--- DataFrame após imputação com Scikit-learn SimpleImputer ---") print(df_filled_sklearn) print(df_filled_sklearn.isnull().sum())📚 Documentação Oficial
SimpleImputer: https://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html
2.2. Tratamento de Duplicatas
Linhas duplicadas podem enviesar seu modelo. É uma boa prática removê-las.
# Vamos adicionar uma linha duplicada para demonstração
df_with_duplicates = pd.concat([df, df.iloc[[0]]], ignore_index=True)
print("\n--- DataFrame com duplicatas ---")
print(df_with_duplicates)
# Remover linhas duplicadas
# Documentação: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.drop_duplicates.html
df_cleaned_duplicates = df_with_duplicates.drop_duplicates()
print("\n--- DataFrame após remoção de duplicatas ---")
print(df_cleaned_duplicates)
print(f"Dimensões após drop_duplicates: {df_cleaned_duplicates.shape}")2.3. Correção de Tipos de Dados (se necessário)
Às vezes, o Pandas pode inferir o tipo errado para uma coluna (ex: números como strings). Você pode corrigir isso com astype().
# Exemplo: Se 'Idade' fosse carregada como string
# df['Idade'] = df['Idade'].astype(int)3. Transformação de Dados: Preparando para o Modelo 🛠️
Após a limpeza, os dados ainda podem precisar de transformações para serem compreendidos pelos algoritmos de ML.
3.1. Codificação de Variáveis Categóricas
Modelos de ML geralmente trabalham apenas com números. Variáveis categóricas (como Cidade, Genero) precisam ser convertidas.
3.1.1. One-Hot Encoding (Variáveis Nominais)
Usado quando não há ordem intrínseca entre as categorias (ex: Cidade, Genero). Cria novas colunas binárias para cada categoria.
-
Com Pandas
pd.get_dummies():df_encoded_pandas = df_filled_sklearn.copy() # Usando o df já imputado # Documentação: https://pandas.pydata.org/docs/reference/api/pandas.get_dummies.html df_encoded_pandas = pd.get_dummies(df_encoded_pandas, columns=['Cidade', 'Genero'], drop_first=True) # drop_first=True evita a multicolinearidade, removendo uma das colunas binárias criadas. print("\n--- DataFrame após One-Hot Encoding com Pandas ---") print(df_encoded_pandas.head()) print(df_encoded_pandas.columns) -
Com Scikit-learn
OneHotEncoder(Melhor prática para pipelines): É preferível para consistência em pipelines de ML.from sklearn.preprocessing import OneHotEncoder from sklearn.compose import ColumnTransformer # Para aplicar diferentes transformações a diferentes colunas df_encoded_sklearn = df_filled_sklearn.copy() # Identificar colunas categóricas para One-Hot Encoding categorical_features = ['Cidade', 'Genero'] # Criar o OneHotEncoder # handle_unknown='ignore' para lidar com categorias que podem aparecer apenas no conjunto de teste one_hot_encoder = OneHotEncoder(handle_unknown='ignore', sparse_output=False) # Aplicar o encoder # Fit e transform nas colunas categóricas encoded_features = one_hot_encoder.fit_transform(df_encoded_sklearn[categorical_features]) # Criar um DataFrame a partir das features codificadas encoded_df = pd.DataFrame(encoded_features, columns=one_hot_encoder.get_feature_names_out(categorical_features), index=df_encoded_sklearn.index) # Remover as colunas categóricas originais e concatenar as codificadas df_encoded_sklearn = df_encoded_sklearn.drop(columns=categorical_features) df_encoded_sklearn = pd.concat([df_encoded_sklearn, encoded_df], axis=1) print("\n--- DataFrame após One-Hot Encoding com Scikit-learn ---") print(df_encoded_sklearn.head()) print(df_encoded_sklearn.columns)📚 Documentação Oficial
OneHotEncoder: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
3.1.2. Label Encoding (Variáveis Ordinais)
Usado quando há uma ordem nas categorias (ex: "Pequeno", "Médio", "Grande"). Atribui um número inteiro único a cada categoria. Cuidado: Muitos algoritmos de ML podem interpretar essa ordem numérica como uma relação de magnitude, o que pode não ser o caso.
from sklearn.preprocessing import LabelEncoder
# Exemplo de dados ordinais (não temos no nosso DF, mas para ilustrar)
# df_ordinal = pd.DataFrame({'Tamanho': ['Pequeno', 'Medio', 'Grande', 'Pequeno']})
# le = LabelEncoder()
# df_ordinal['Tamanho_Encoded'] = le.fit_transform(df_ordinal['Tamanho'])
# print("\n--- Exemplo de Label Encoding ---")
# print(df_ordinal)📚 Documentação Oficial LabelEncoder: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html
3.2. Escalonamento de Variáveis Numéricas
Muitos algoritmos de ML (como Regressão Logística, SVMs, Redes Neurais) são sensíveis à escala das features. Escalar os dados garante que nenhuma feature domine as outras devido à sua magnitude.
3.2.1. Padronização (Standardization) com StandardScaler
Transforma os dados para ter média 0 e desvio padrão 1 (distribuição normal). Útil quando a distribuição é aproximadamente gaussiana ou quando há outliers.
from sklearn.preprocessing import StandardScaler
df_scaled_standard = df_encoded_sklearn.copy() # Usando o df já imputado e codificado
# Identificar colunas numéricas para escalonamento
numerical_features = ['Idade', 'Salario', 'Experiencia_Anos']
# Criar o StandardScaler
scaler = StandardScaler()
# Aplicar o scaler
df_scaled_standard[numerical_features] = scaler.fit_transform(df_scaled_standard[numerical_features])
print("\n--- DataFrame após Padronização (StandardScaler) ---")
print(df_scaled_standard.head())
print(df_scaled_standard.describe()) # Média próxima de 0, Std próxima de 1📚 Documentação Oficial StandardScaler: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html
3.2.2. Normalização (Normalization) com MinMaxScaler
Transforma os dados para um intervalo fixo, geralmente entre 0 e 1. Útil quando você precisa de valores em um intervalo limitado ou quando a distribuição não é gaussiana.
from sklearn.preprocessing import MinMaxScaler
df_scaled_minmax = df_encoded_sklearn.copy() # Usando o df já imputado e codificado
# Identificar colunas numéricas para escalonamento
# As mesmas features numéricas
numerical_features = ['Idade', 'Salario', 'Experiencia_Anos']
# Criar o MinMaxScaler
minmax_scaler = MinMaxScaler()
# Aplicar o scaler
df_scaled_minmax[numerical_features] = minmax_scaler.fit_transform(df_scaled_minmax[numerical_features])
print("\n--- DataFrame após Normalização (MinMaxScaler) ---")
print(df_scaled_minmax.head())
print(df_scaled_minmax.describe()) # Min 0, Max 1📚 Documentação Oficial MinMaxScaler: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html
4. Divisão dos Dados: Treino e Teste 🧪
Antes de treinar qualquer modelo, é crucial dividir seu dataset em conjuntos de treino e teste. O conjunto de treino é usado para ensinar o modelo, e o conjunto de teste é usado para avaliar seu desempenho em dados não vistos. Isso ajuda a evitar o overfitting.
from sklearn.model_selection import train_test_split
# Vamos usar o DataFrame final pré-processado (df_scaled_standard)
X = df_scaled_standard.drop('Target', axis=1) # Features
y = df_scaled_standard['Target'] # Variável alvo
# Documentação: https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# test_size=0.2: 20% dos dados para teste, 80% para treino
# random_state=42: Garante que a divisão seja a mesma a cada execução (reprodutibilidade)
# stratify=y: Garante que a proporção das classes da variável 'y' seja mantida em ambos os conjuntos
print("\n--- Divisão dos Dados em Treino e Teste ---")
print(f"Dimensões de X_train: {X_train.shape}")
print(f"Dimensões de X_test: {X_test.shape}")
print(f"Dimensões de y_train: {y_train.shape}")
print(f"Dimensões de y_test: {y_test.shape}")
print("\nProporção das classes em y original:")
print(y.value_counts(normalize=True))
print("\nProporção das classes em y_train:")
print(y_train.value_counts(normalize=True))
print("\nProporção das classes em y_test:")
print(y_test.value_counts(normalize=True))A opção stratify=y é especialmente importante para problemas de classificação, garantindo que a distribuição das classes na variável alvo (y) seja proporcionalmente a mesma nos conjuntos de treino e teste.
5. Exercícios Práticos: Mão na Massa! 🚀
Agora é a sua vez de aplicar o que aprendemos! Você vai trabalhar com um dataset real (ou um simulado, se preferir) e passar por todas as etapas de exploração e pré-processamento.
📚 Dataset Sugerido: Titanic Dataset
O dataset do Titanic é um clássico para iniciantes em Machine Learning. O objetivo é prever a sobrevivência de passageiros.
Você pode baixá-lo diretamente do Kaggle ou usar a URL abaixo:
https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv
📋 Tarefas do Projeto:
Crie um novo script Python (ex: preprocessing_titanic.py) e siga os passos abaixo:
-
1. Carregamento e Exploração Inicial:
- Carregue o dataset do Titanic usando Pandas.
- Utilize
head(),info(),describe(),isnull().sum()edtypespara obter uma compreensão inicial dos dados. - Identifique colunas com valores ausentes.
- Identifique colunas categóricas e numéricas.
-
2. Tratamento de Valores Ausentes:
- Para a coluna
Age(Idade), preencha os valores ausentes com a mediana da coluna, usandoSimpleImputerdo Scikit-learn. - Para a coluna
Embarked(Porto de Embarque), preencha os valores ausentes com a moda (o valor mais frequente) da coluna, usandoSimpleImputerdo Scikit-learn. - Para a coluna
Cabin(Cabine), como possui muitos valores ausentes, decida se é melhor removê-la ou criar uma nova feature indicando a presença/ausência de cabine. Para este exercício, remova a colunaCabin. - Verifique novamente
isnull().sum()para confirmar que não há mais valores ausentes nas colunas tratadas.
- Para a coluna
-
3. Tratamento de Duplicatas:
- Verifique se há linhas duplicadas no dataset e remova-as, se existirem.
-
4. Codificação de Variáveis Categóricas:
- Identifique as colunas categóricas que precisam ser codificadas (
Sex,Embarked,Pclass). - Aplique
OneHotEncoderdo Scikit-learn para codificar essas colunas. Lembre-se de remover as colunas originais e concatenar as novas colunas codificadas.
- Identifique as colunas categóricas que precisam ser codificadas (
-
5. Escalonamento de Variáveis Numéricas:
- Identifique as colunas numéricas que precisam ser escalonadas (
Age,Fare,SibSp,Parch). - Aplique
StandardScalerdo Scikit-learn para padronizar essas colunas.
- Identifique as colunas numéricas que precisam ser escalonadas (
-
6. Divisão dos Dados:
- Separe as features (X) da variável alvo (y), que é
Survived. - Divida o dataset pré-processado em conjuntos de treino e teste usando
train_test_split, comtest_size=0.2,random_state=42estratify=y.
- Separe as features (X) da variável alvo (y), que é
-
7. Verificação Final:
- Imprima as primeiras linhas do seu
X_traineX_testpara ver o resultado final do pré-processamento. - Verifique as dimensões de todos os conjuntos (
X_train,X_test,y_train,y_test).
- Imprima as primeiras linhas do seu
Este exercício é crucial para solidificar seu entendimento das etapas de pré-processamento. Lembre-se de consultar a documentação oficial do Pandas e Scikit-learn sempre que tiver dúvidas!
6. Resumo e Próximos Passos 🏁
Parabéns! Você concluiu uma das etapas mais importantes e demoradas em qualquer projeto de Machine Learning. Dominar a exploração e o pré-processamento de dados é o que diferencia um bom cientista de dados.
Nesta aula, você aprendeu a:
- Carregar e inspecionar dados com Pandas.
- Lidar com valores ausentes usando
dropna(),fillna()e o poderosoSimpleImputerdo Scikit-learn. - Remover duplicatas.
- Converter variáveis categóricas em numéricas usando
pd.get_dummies()eOneHotEncoderdo Scikit-learn. - Escalonar variáveis numéricas com
StandardScalereMinMaxScalerdo Scikit-learn. - Dividir seus dados em conjuntos de treino e teste com
train_test_split.
Com seus dados agora limpos e transformados, você está pronto para a próxima fase do projeto: Seleção e Treinamento de Modelos de Machine Learning! Na próxima aula, exploraremos diferentes algoritmos e como aplicá-los aos seus dados pré-processados.
Continue praticando, e você se tornará um mestre na arte da preparação de dados! ✨