Um guia (simples) de como tratar variáveis categóricas em Machine Learning

Nelson Ewert
6 min readAug 25, 2020

--

O que são variáveis categóricas?

Uma das etapas que mais demanda tempo ao trabalhar com modelos de machine learning é o tratamento dos dados. Ela também é essencial para dar sentido a qualquer conclusão que venhamos a tirar dos dados.

Dentre os tipos de variáveis que podemos encontrar ao tratar os dados, existem as numéricas, ou quantitativas (por exemplo: peso, comprimento, temperatura), e as categóricas, ou qualitativas (por exemplo: gênero, nacionalidade). É com o segundo tipo que nós lidaremos nesse artigo.

As variáveis categóricas também possuem uma divisão entre si. Os exemplos dados (gênero, nacionalidade) são variáveis categóricas nominais, pois não possuem uma hierarquia entre si. Mas quando falamos de variáveis como nível de escolaridade, estamos lidando com variáveis categóricas ordinais.

Os tipos de dados existentes. Nosso foco são os da direita do diagrama.

Tá, e o que eu faço com elas?

Antes de tudo, você vai precisar deixar as variáveis de um jeito que o modelo entenda. Ou seja, sem valores nulos (NaN) e com as variáveis de entrada em formato numérico. E como transformar uma variável categórica em numérica? Já chegaremos lá…

Tratando os valores nulos

É comum que uma base de dados venha com alguns valores nulos. Entretanto, ao escolher as variáveis que serão usadas no modelo é importante que elas não tenham muitos valores faltando, ou seja, que esses valores sejam a exceção e não a regra.

Mesmo esses poucos valores nulos que sobrarem na sua base (ou nas colunas da base que serão usadas para criar o modelo) precisam ser preenchidos, em um processo chamado de imputação.

Para as variáveis numéricas, é comum preencher os valores em branco com a média ou a mediana dos valores da coluna. Mas quando falamos de dados categóricos, não faz sentido falarmos de média ou mediana, certo? Por isso, usamos a moda, que é o valor de maior frequência.

Por exemplo, vamos observar os dados da competição do kaggle Titanic: machine learning from disaster, onde se tem como objetivo construir um modelo para prever quais passageiros sobreviveram após o Titanic afundar.

Ao listarmos as colunas do dataset de treinamento:

IN [1]:
import pandas as pd
train_data = pd.read_csv(‘/kaggle/input/titanic/train.csv’)
train_data.info()
OUT [1]:<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 891 non-null int64
1 Survived 891 non-null int64
2 Pclass 891 non-null int64
3 Name 891 non-null object
4 Sex 891 non-null object
5 Age 714 non-null float64
6 SibSp 891 non-null int64
7 Parch 891 non-null int64
8 Ticket 891 non-null object
9 Fare 891 non-null float64
10 Cabin 204 non-null object
11 Embarked 889 non-null object
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB

Observamos as variáveis com o tipo object. A coluna 10, Cabin, representa a cabine na qual o passageiro viajava. Porém ela só possui 204 valores não-nulos, ou seja, a maioria dos valores da coluna está em branco. Isso indica que não podemos usar essa variável na construção do modelo preditivo.

Já a variável 11, Embarked, representa o porto no qual o passageiro embarcou. Sem entrar no mérito dessa informação ser relevante para indicar se uma pessoa sobreviveu à tragédia ou não, suponhamos que a gente queira usar essa variável no modelo. Ela possui 889 valores não nulos, ou seja, 2 valores nulos.

É perfeitamente aceitável, nesse caso, imputar para esses dois passageiros (os quais não sabemos em que porto embarcaram) o porto que mais se repete entre os outros passageiros, ou seja, a moda dos valores. No python, ela é dada pela função mode().

IN [2]:
train_data[‘Embarked’]=train_data[‘Embarked’].fillna(train_data[‘Embarked’].mode()[0])

Convertendo os dados categóricos em numéricos

Existem duas abordagens para converter os dados categóricos em numéricos, para que o modelo possa entendê-los. Veremos dois métodos: Label encoding e One-Hot encoding.

Label Encoding

Esse método é recomendado quando lidamos com variáveis categóricas ordinais.

Consiste em designar um valor inteiro (0, 1, 2, …) para cada valor único da coluna. Por exemplo, caso tenhamos uma base que contenha a frequência com a qual um grupo de pessoas toma café-da-manhã, variando de nunca (never) a todo dia (every day), o label encoding fará o seguinte:

Para realizar o label encoding no Python, usamos a classe LabelEncoder do sklearn.preprocessing.

No código exemplo abaixo, considere que já temos os dados de treinamento e teste separados em X_train, X_test, y_train, ey_test:

IN [3]:
from sklearn.preprocessing import LabelEncoder
# Criar uma lista de variáveis ordinais:
object_cols = [variavel_1, variavel_2, ... ]
label_encoder = LabelEncoder()
for col in object_cols:
label_X_train[col] = label_encoder.fit_transform(X_train[col])
label_X_test[col] = label_encoder.transform(X_test[col])

Na nossa base da competição do Titanic do kaggle, há apenas uma variável categórica ordinal: Pclass, que representa a classe do passageiro. Porém ela já está “encodada”, com os inteiros 1, 2 e 3 representando respectivamente a primeira, segunda e terceira classes do navio.

Para as variáveis nominais, O Label Encoding não é recomendado porque o modelo pode entender que existe alguma ordem ou hierarquia entre os valores, e por isso dar pesos diferentes a eles. Por isso, utilizamos o One-Hot encoding.

One-Hot encoding

Consiste em criar uma nova coluna para cada valor único de uma coluna existente. Esse conceito ficará claro no exemplo mais abaixo.

Também pode ser usado nas variáveis categóricas ordinais, mas lembre-se que cada valor único vai virar uma coluna nova, então essa estratégia pode deixar o seu modelo muito pesado se as variáveis tiverem muitos valores únicos.

Existem duas formas simples de fazer o One-Hot encoding no python: a função pandas.get_dummies e a classe OneHotEncoder do sklearn.preprocessing.

Existem algumas diferenças entre os dois métodos, não vamos entrar muito a fundo na explicação. Aqui, daremos um exemplo usando o get_dummies, de novo para a base do Titanic:

IN [4]:
# Usamos um pequeno truque de transformar a coluna Pclass em string, para que ela também seja transformada pelo método:
train_data[‘Pclass’] = train_data[‘Pclass’].astype(str)# Outro truque: agrupamos a coluna Age em grupos etários, para que não haja uma coluna para cada idade diferente:train_data[“Age”] = pd.cut(train_data[“Age”],bins=[10,20,30,40,50,60,70,80])# Selecionamos as colunas que queremos usar no modelo:features = [‘Sex’, ‘SibSp’, ‘Parch’, ‘Pclass’, ‘Age’]
X = train_data[features]
# Aplicamos o get_dummies. A explicação para o parâmetro drop_first será dada abaixo.X = pd.get_dummies(X, drop_first = True)
X.head()
Observe como ficaram as colunas após aplicarmos o One-Hot encoding

Vamos analisar o que aconteceu com cada uma das colunas:

  • SibSp e Parch: eram numéricas, a primeira indicando o número de irmãos e/ou cônjuges e a segunda o número de pais e/ou filhos que o passageiro tinha a bordo do navio. Permaneceram inalteradas.
  • Sex: era uma coluna binária (M/F). Foi dividida em Sex_male e Sex_female.
  • PClass: Se não tivéssemos feito nada, essa coluna teria sido tratada como numérica. Como queríamos transformá-la em “dummies”, primeiro mudamos o tipo dos valores para string. Foi dividida em 3.
  • Age: Também era numérica, nós a dividimos em grupos etários que o get_dummies separou em 7 colunas.

Você pode ter reparado que as colunas Sex_female, Pclass_1 e Age_[0,20) sumiram. Isso ocorre porque passamos o parâmetro drop_first = True para o get_dummies. Por que?

Simples! essas colunas, após serem modificadas, ficam redundantes. Pense comigo: Se a coluna Sex só admite dois valores (M/F), Eu não preciso de uma coluna indicando se o passageiro é homem e outra coluna indicando se é mulher. Qualquer uma das duas já contém toda a informação.

Sempre que aplicamos o One-Hot encoding em uma coluna, ficamos com uma coluna redundante que pode ser descartada. Qual? Qualquer uma! Descartamos a primeira porque a função get_dummies nos permite fazer isso simplesmente passando um parâmetro.

Considerações finais

Só com a experiência você vai saber quando usar o Label encoding, quando utilizar o One-Hot encoding ou quando simplesmente descartar a coluna. Mas algumas dicas, que podem ter passado batido no artigo, podem ajudar:

  • Se a coluna tiver muitos valores nulos, descarte-a;
  • Dê preferência ao One-Hot encoding. Exceto se a coluna tiver muitos valores únicos diferentes. Nesse caso, considere agrupá-los (como fizemos com as idades no exemplo).
  • Se a variável for ordinal, considere usar um Label Encoding em vez do One-Hot encoding. Ou teste ambos e veja o que funciona melhor no seu caso.

--

--