Pré-processamento de uma base de dados usando o conjunto Iris
Antes de treinarmos qualquer modelo de machine learning, existe uma etapa muito importante com a qual devemos ficar atentos: a preparação dos dados. Em projetos reais, os dados frequentemente possuem valores ausentes, escalas diferentes, categorias textuais, erros de digitação, colunas irrelevantes e distribuições desequilibradas. Por isso, a exploração da base e o pré-processamento são etapas fundamentais em ML.
O próprio scikit-learn organiza recursos específicos para pré-processamento, como padronização de variáveis, imputação de valores ausentes e codificação de atributos categóricos (Scikit-learn, 2026).
Preparando a base de dados Iris
No código abaixo vamos usar o dataset Iris, que é uma base clássica para quem trabalha com IA e Ciência de Dados. Usado pela primeira vez por Fisher (1936), o conjunto de dados foi coletado pelo botânico Edgar Anderson para quantificar a variação morfológica de três espécies de íris.
O conjunto de dados contém 3 classes com 50 instâncias cada, onde cada classe se refere a um tipo de planta de íris. Suas principais caracteríticas são:
| Classes | 03 (Iris-Setosa, Iris-Versicolour, Iris-Virginica) |
| Amostras por classe | 50 |
| Amostras totais | 150 |
| Dimensionalidade (quantidade de colunas) | 04 |
| Tipo dos dados | Reais positivos |
| Nomes dos atributos | sepal length – Comprimento da sépala (cm) sepal width – Largura da sépala (cm) petal length – Comprimento da pétala (cm) petal width – Largura da pétala (cm) |
Como a base Iris é clássica, ela já está bem construída e organizada. Por isso, vamos criar artificialmente alguns valores nulos e uma coluna categórica para que possamos praticar etapas comuns de preparação de dados: carregamento, análise exploratória, tratamento de nulos, codificação com LabelEncoder e normalização com StandardScaler.
Porém, antes de começar, um disclaimer: o foco aqui não é treinar um modelo preditivo completo ainda, pois estamos discutindo a compreensão dos dados antes da modelagem.
Carregando as bibliotecas e a base de dados
Vamos começar carregando as bibliotecas que usaremos durante todo o código. O dataset Iris foi importado a partir do scikit-learn e depois carregado com load_iris(as_frame=True). Essa escolha permite trabalhar com os dados em formato de DataFrame, estrutura do pandas adequada para manipulação tabular.
O dataset Iris é um exemplo clássico de problema supervisionado, pois cada flor possui atributos mensuráveis e uma espécie associada. Portanto, ele representa bem a ideia de aprender uma função que associa entradas a saídas conhecidas.
# ------------------------------------------------------------
# Carregando as bibliotecas e a base de dados
# ------------------------------------------------------------
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.impute import SimpleImputer
# ------------------------------------------------------------
# Carregamento do dataset Iris
# ------------------------------------------------------------
iris = load_iris(as_frame=True)“Poluindo” as classes e inserindo valores nulos
Em seguida, criamos a coluna especie com valores textuais. Estamos fazendo isso para tornar a base mais parecida com situações reais, nas quais as categorias muitas vezes aparecem como texto (neste caso, “setosa”, “versicolor” e “virginica”). Contudo, destacamos que essa coluna será posteriormente codificada, pois muitos algoritmos de machine learning trabalham melhor com valores numéricos.
dados = iris.frame.copy()
# A coluna target vem como número. Vamos criar também uma coluna textual para representar o nome da espécie da flor.
dados["especie"] = dados["target"].map({
0: "setosa",
1: "versicolor",
2: "virginica"
})
# Removemos a coluna target original para simular um cenário mais didático, no qual a classe aparece como texto.
dados = dados.drop(columns=["target"])
print("\nPrimeiras linhas do dataset:")
print(dados.head())Continuando, vamos inserir uns valores nulos na base. A simulação de dados nulos serve para mostrar um problema frequente em ciência de dados, já que bases reais raramente chegam prontas para uso. É muito comum faltar respostas em formulários, sensores podem falhar, registros podem ser preenchidos incorretamente e bancos de dados podem ter campos incompletos. Por isso, o código introduz valores Not a Number (NaN) em colunas numéricas e categóricas.
# ------------------------------------------------------------
# Simulação de dados ausentes
# ------------------------------------------------------------
# Em bases reais, valores ausentes são comuns. Aqui vamos inserir valores nulos artificialmente para fins didáticos.
dados.loc[3, "sepal length (cm)"] = np.nan
dados.loc[7, "petal width (cm)"] = np.nan
dados.loc[12, "especie"] = np.nan
print("\nQuantidade de valores nulos por coluna:")
print(dados.isnull().sum())Beleza! Agora que cometemos a heresia de “poluir” a base de dados (mas foi por uma boa causa hahaha), partiremos para a análise exploratória. Comandos como head(), info(), describe() e value_counts() serão importantes para nós.
Aprendendo a estrutura da base de dados
Essa etapa corresponde ao momento em que começamos a compreender a estrutura dos dados. Antes de aplicar qualquer algoritmo de aprendizado de máquina, é preciso saber quantas colunas existem, quais são numéricas, quais são categóricas, quais possuem valores ausentes e como os valores estão distribuídos.
# ------------------------------------------------------------
# Análise exploratória simples
# ------------------------------------------------------------
print("\nInformações gerais da base:")
print(dados.info())
print("\nEstatísticas descritivas das colunas numéricas:")
print(dados.describe())
print("\nFrequência das espécies:")
print(dados["especie"].value_counts(dropna=False))O resultado esperado para o código criado até o momento é algo como o descrito abaixo. A partir dos métodos info(), describe() e value_counts() conseguimos descobrir o tipo dos dados (float64, e object), a contagem máxima de cada coluna, a quantidade de memória sendo utilizada, as estatísticas descritivas (média, desvio padrão etc.) e as frequências das espécies.
Informações gerais da base:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 sepal length (cm) 149 non-null float64
1 sepal width (cm) 150 non-null float64
2 petal length (cm) 150 non-null float64
3 petal width (cm) 149 non-null float64
4 especie 149 non-null object
dtypes: float64(4), object(1)
memory usage: 6.0+ KB
None
Estatísticas descritivas das colunas numéricas:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
count 149.000000 150.000000 150.000000 149.000000
mean 5.851678 3.057333 3.758000 1.206040
std 0.824507 0.435866 1.765298 0.760354
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000
Frequência das espécies:
especie
virginica 50
versicolor 50
setosa 49
NaN 1
Name: count, dtype: int64Tratando os valores nulos com SimpleImputer
Com o conhecimento mais claro sobre o conjunto de dados, partiremos para a etapa de tratamento dos valores nulos. Segundo a documentação do scikit-learn, o SimpleImputer substitui valores ausentes usando estatísticas descritivas, como média, mediana ou valor mais frequente (Scikit-learn, 2026). Para as colunas numéricas, o código usa a média. Para a coluna categórica, usa a categoria mais frequente (a moda).
# ------------------------------------------------------------
# Separação entre atributos numéricos e categóricos
# ------------------------------------------------------------
colunas_numericas = [
"sepal length (cm)",
"sepal width (cm)",
"petal length (cm)",
"petal width (cm)"
]
coluna_categorica = "especie"
# ------------------------------------------------------------
# Tratamento de valores nulos
# ------------------------------------------------------------
# Para variáveis numéricas, vamos preencher valores ausentes com a média.
imputador_numerico = SimpleImputer(strategy="mean")
dados[colunas_numericas] = imputador_numerico.fit_transform(
dados[colunas_numericas]
)
# Para a variável categórica, vamos preencher com a classe mais frequente.
imputador_categorico = SimpleImputer(strategy="most_frequent")
dados[[coluna_categorica]] = imputador_categorico.fit_transform(
dados[[coluna_categorica]]
)
print("\nValores nulos após o tratamento:")
print(dados.isnull().sum())Após a imputação da média e da moda, o algoritmo retornará a quantidade de valores nulos que temos na base. Obviamente, estamos esperando que o resultado seja igual ao apresentado a seguir:
Valores nulos após o tratamento:
sepal length (cm) 0
sepal width (cm) 0
petal length (cm) 0
petal width (cm) 0
especie 0
dtype: int64Codificando a saída esperada com LabelEncoder
Seguindo nossa lógica, vamos ajustar o tipo da coluna especie, que representa a saída esperada (ou classe a ser prevista) dos registros. Para a codificação estamos usando o LabelEncoder, que vai transformar os nomes das espécies em números. A documentação do scikit-learn observa que o LabelEncoder é usado para codificar rótulos-alvo em valores entre 0 e n_classes - 1, sendo adequado para a variável de saída y – que é a nossa coluna especie. (Scikit-learn, 2026).
# ------------------------------------------------------------
# Codificação da variável categórica com LabelEncoder
# ------------------------------------------------------------
codificador = LabelEncoder()
dados["especie_codificada"] = codificador.fit_transform(
dados["especie"]
)
print("\nClasses aprendidas pelo LabelEncoder:")
print(list(codificador.classes_))
print("\nExemplo de espécies codificadas:")
print(dados[["especie", "especie_codificada"]].head())O LabelEncoder vai aprender a quantidade de classes disponíveis na coluna especie e partir dela, fazer a associação dos valores de 0 a n_classes - 1. O resultado que ele apresenta ao final indica que o método foi capaz de detectar as três classes esperadas: ‘setosa‘, ‘versicolor‘, ‘virginica‘.
Classes aprendidas pelo LabelEncoder:
['setosa', 'versicolor', 'virginica']
Exemplo de espécies codificadas:
especie especie_codificada
0 setosa 0
1 setosa 0
2 setosa 0
3 setosa 0
4 setosa 0Padronizando os dados com StandardScaler
A documentação do scikit-learn descreve o StandardScaler como uma técnica que remove a média e escala os dados para variância unitária (Scikit-learn, 2026). Em outras palavras, a padronização com StandardScaler transforma as variáveis numéricas para terem média aproximadamente 0 e desvio padrão aproximadamente 1. Logo, a padronização com o StandardScaler é a implementação prática da normalização por z-score.
Essa etapa é importante porque muitos algoritmos de machine learning são sensíveis à escala das variáveis (SVM, K-Means, KNN, Regressão Linear, Regressão Logística, Redes Neurais etc.). Podemos usar o StandardScaler quando assumimos que os dados seguem uma distribuição normal (gaussiana) ou quando os algoritmos de machine learning não assumem uma distribuição específica, mas exigem padronização.
# ------------------------------------------------------------
# Normalização/padronização das variáveis numéricas
# ------------------------------------------------------------
normalizador = StandardScaler()
dados_normalizados = dados.copy()
dados_normalizados[colunas_numericas] = normalizador.fit_transform(
dados[colunas_numericas]
)
print("\nDados após padronização das colunas numéricas:")
print(dados_normalizados.head())O resultado obtido pelo método será:
Dados após padronização das colunas numéricas:
sepal length (cm) sepal width (cm) ... especie especie_codificada
0 -0.917809 1.019004 ... setosa 0
1 -1.162012 -0.131979 ... setosa 0
2 -1.406215 0.328414 ... setosa 0
3 0.000000 0.098217 ... setosa 0
4 -1.039910 1.249201 ... setosa 0
[5 rows x 6 columns]As poucas informações que mostramos no algoritmo não representam corretamente o funcionamento do StandardScaler, mas como ele transforma os dados em Z-scores, o número final indica a quantas “unidades de desvio padrão” aquele dado está da média. Por exemplo:
- 0: O valor é exatamente a média.
- 1: O valor está 1 desvio padrão acima da média.
- -2: O valor está 2 desvios padrão abaixo da média.
- 4.5: O valor é um outlier (está muito longe da média).
Na saída vemos que a maior parte dos 5 primeiros registros das colunas sepal length (cm) e sepal width (cm) está bem próximo da média, para cima ou para baixo, mas não estamos vendo o contexto completo. Por isso, não julgue que sempre os valores sairão entre 0 e 1, beleza?
Finalizando o pré-processamento
A parte final do algoritmo é dedicada a separar os atributos de entrada ( X ), isto é, as informações usadas pelo modelo para aprender e a resposta esperada ( y ), ou seja, aquilo que o modelo tentará prever. Essa separação concretiza a estrutura conceitual do aprendizado supervisionado.
# ------------------------------------------------------------
# Resultado final preparado para futuros modelos
# ------------------------------------------------------------
X = dados_normalizados[colunas_numericas]
y = dados_normalizados["especie_codificada"]
print("\nFormato dos atributos de entrada X:")
print(X.shape)
print("\nFormato do alvo y:")
print(y.shape)
print("\nAmostra de X:")
print(X.head())
print("\nAmostra de y:")
print(y.head())As últimas informações impressas pelo código no terminal serão:
Formato dos atributos de entrada X:
(150, 4)
Formato do alvo y:
(150,)
Amostra de X:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 -0.917809 1.019004 -1.340227 -1.33203
1 -1.162012 -0.131979 -1.340227 -1.33203
2 -1.406215 0.328414 -1.397064 -1.33203
3 0.000000 0.098217 -1.283389 -1.33203
4 -1.039910 1.249201 -1.340227 -1.33203
Amostra de y:
0 0
1 0
2 0
3 0
4 0
Name: especie_codificada, dtype: int64Elas indicam como está a estrutura das entradas X (150 linhas e 4 colunas) e das saídas esperadas y (150 linhas e 1 coluna). Além disso, com o comando head() mostramos as 5 primeiras linhas das amostras de entrada e saída.
Conclusão
Após a leitura deste artigo podemos concluir que implementar machine learning começa antes do modelo propriamente dito. A etapa inicial é compreender o problema, analisar os dados, identificar variáveis, tratar inconsistências e preparar a base para que o modelo possa aprender de forma adequada.
Independentemente do tipo de aprendizado (supervisionado, não supervisionado ou por reforço), todos dependem de uma boa representação do problema. É necessário compreender o que os dados representam, que tipo de problema está sendo resolvido, quais hipóteses o algoritmo assume e quais decisões humanas foram tomadas durante o pré-processamento.
Obrigado pela leitura e bons estudos!
Referências
SCIKIT-LEARN. API Reference. Documentação oficial da biblioteca com informações para uso dos métodos, classes e elementos relacionados. Disponível em: <https://scikit-learn.org/stable/api/index.html>. Acesso em: 1 maio 2026.


