Engenharia de Software para Ciência de Dados - PUC-Rio¶

Seleção de Atributos¶

Marcos Kalinowski e Tatiana Escovedo

Para voltar a atividade 2 link text https://colab.research.google.com/drive/1MCCMFdsGaXIQjdAYpFrqTyw0bvtXhuYb#scrollTo=3UTQu_c6g3Ut

In [1]:
# Imports
import pandas as pd
import numpy as np
from numpy import set_printoptions
import matplotlib.pyplot as plt
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import ExtraTreesClassifier

# Novos imports
from sklearn.feature_selection import SelectKBest # para a Seleção Univariada
from sklearn.feature_selection import f_classif # para o teste ANOVA da Seleção Univariada
from sklearn.feature_selection import RFE # para a Eliminação Recursiva de Atributos

Carga do dataset¶

In [2]:
# importando dados de um arquvo  crv para dataframe vinhoreddados : VINHO TINTO
 # url a importar
url_dados_wred = 'https://wdz.eng.br/Curso/Atividade2/arquivos/winequality-red.csv'
#rotulos dos atributos do dadtaset
atributosred = ['acidezfixa', 'acidezvolátil', 'ácidocítrico', 'acucarresidual', 'cloretos', 'dióxidoenxofrelivre', 'dióxidoenxofretotal', 'densidade', 'pH', 'sulfatos', 'alcool']
#carga do dataset
vinhoreddados = pd.read_csv(url_dados_wred, delimiter=';')
In [ ]:
# Carrega arquivo csv usando Pandas usando uma URL

# Informa a URL de importação do dataset
url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv"

# Informa o cabeçalho das colunas
colunas = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']

# Lê o arquivo utilizando as colunas informadas
dataset = pd.read_csv(url, names=colunas, skiprows=0, delimiter=',')

Preparação de dados¶

In [3]:
# separando os atributos e a classe do dataset
array = vinhoreddados.values
X = array[:,0:8]
y = array[:,8]

# A semente (seed) pode ser qualquer número, e garante que os resultados possam ser reproduzidos de forma idêntica toda vez que o script for rodado. 
# Isto é muito importante quando trabalhamos com modelos ou métodos que utilizam de algum tipo de aleatoriedade.
seed = 7
In [8]:
print (X)
print (y)
[[7.400e+00 7.000e-01 0.000e+00 ... 1.100e+01 3.400e+01 9.978e-01]
 [7.800e+00 8.800e-01 0.000e+00 ... 2.500e+01 6.700e+01 9.968e-01]
 [7.800e+00 7.600e-01 4.000e-02 ... 1.500e+01 5.400e+01 9.970e-01]
 ...
 [6.300e+00 5.100e-01 1.300e-01 ... 2.900e+01 4.000e+01 9.957e-01]
 [5.900e+00 6.450e-01 1.200e-01 ... 3.200e+01 4.400e+01 9.955e-01]
 [6.000e+00 3.100e-01 4.700e-01 ... 1.800e+01 4.200e+01 9.955e-01]]
[3.51 3.2  3.26 ... 3.42 3.57 3.39]

Seleção de Atributos¶

https://scikit-learn.org/stable/modules/feature_selection.html#feature-selection

Seleção Univariada¶

https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectKBest.html

A função SelectKBest() pode ser usada com diversos testes estatísticos para selecionar os atributos. Vamos usar o teste ANOVA F-value e selecionar os 4 melhores atributos que podem ser usados como variáveis preditoras.

Neste contexto, o teste ANOVA F-value estima o quanto cada característica de X é dependente da classe y.

Para saber mais: https://www.statisticshowto.com/probability-and-statistics/f-statistic-value-test/

In [5]:
# Função para seleção de atributos
best_var = SelectKBest(score_func=f_classif, k=4)

# Executa a função de pontuação em (X, y) e obtém os atributos selecionados
fit = best_var.fit(X, y)

# Reduz X para os atributos selecionados
features = fit.transform(X)

# Resultados
print('\nNúmero original de atributos:', X.shape[1])
print('\nNúmero reduzido de atributos:', features.shape[1])

# Exibe os atributos orginais
print("\nAtributos Originais:", vinhoreddados.columns[0:8])

# Exibe as pontuações de cada atributos e os 4 escolhidas (com as pontuações mais altas): fixed acidity' 'citric acid' 'chlorides' 'density'.
set_printoptions(precision=3) # 3 casas decimais
print("\nScores dos Atributos Originais:", fit.scores_)
print("\nAtributos Selecionados:", best_var.get_feature_names_out(input_features=vinhoreddados.columns[0:8]))

# Imprime o dataset apenas com as colunas selecionadas
print("\n", features)
Número original de atributos: 8

Número reduzido de atributos: 4

Atributos Originais: Index(['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar',
       'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density'],
      dtype='object')

Scores dos Atributos Originais: [20.561  2.816  8.934  1.219  6.36   1.388  1.573  5.109]

Atributos Selecionados: ['fixed acidity' 'citric acid' 'chlorides' 'density']

 [[7.4   0.    0.076 0.998]
 [7.8   0.    0.098 0.997]
 [7.8   0.04  0.092 0.997]
 ...
 [6.3   0.13  0.076 0.996]
 [5.9   0.12  0.075 0.995]
 [6.    0.47  0.067 0.995]]

Eliminação Recursiva de Atributos¶

https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html

Iremos aplicar a técnica de eliminação recursiva de atributos com um algoritmo de Regressão Logística (poderia ser qualquer classificador) para selecionar as 4 melhores variáveis preditoras.

In [7]:
# Criação do modelo
modelo = LogisticRegression(max_iter=200)

# Eliminação Recursiva de Variáveis
rfe = RFE(modelo, n_features_to_select=4)
fit = rfe.fit(X, y)

# Print dos resultados
print("Atributos Originais:", vinhoreddados.columns[0:8])

# Exibe os atributos selecionados (marcados como True em "Atributos Selecionados" 
# e com valor 1 em "Ranking dos Atributos"): fixed acidity' 'citric acid' 'chlorides' 'density'.
# (Basta mapear manualmente o índice dos nomes dos respectivos atributos)
print("\nAtributos Selecionados: %s" % fit.support_)
print("\nRanking de atributos: %s" % fit.ranking_)
print("\nQtd de melhores Atributos: %d" % fit.n_features_)
print("\nNomes dos Atributos Selecionados: %s" % fit.get_feature_names_out(input_features=vinhoreddados.columns[0:8]))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-7-1e36d9a882e0> in <module>
      4 # Eliminação Recursiva de Variáveis
      5 rfe = RFE(modelo, n_features_to_select=4)
----> 6 fit = rfe.fit(X, y)
      7 
      8 # Print dos resultados

/usr/local/lib/python3.8/dist-packages/sklearn/feature_selection/_rfe.py in fit(self, X, y, **fit_params)
    220             Fitted estimator.
    221         """
--> 222         return self._fit(X, y, **fit_params)
    223 
    224     def _fit(self, X, y, step_score=None, **fit_params):

/usr/local/lib/python3.8/dist-packages/sklearn/feature_selection/_rfe.py in _fit(self, X, y, step_score, **fit_params)
    281                 print("Fitting estimator with %d features." % np.sum(support_))
    282 
--> 283             estimator.fit(X[:, features], y, **fit_params)
    284 
    285             # Get importance and rank them

/usr/local/lib/python3.8/dist-packages/sklearn/linear_model/_logistic.py in fit(self, X, y, sample_weight)
   1514             accept_large_sparse=solver not in ["liblinear", "sag", "saga"],
   1515         )
-> 1516         check_classification_targets(y)
   1517         self.classes_ = np.unique(y)
   1518 

/usr/local/lib/python3.8/dist-packages/sklearn/utils/multiclass.py in check_classification_targets(y)
    195         "multilabel-sequences",
    196     ]:
--> 197         raise ValueError("Unknown label type: %r" % y_type)
    198 
    199 

ValueError: Unknown label type: 'continuous'

Importância de Atributos com ExtraTrees¶

http://scikit-learn.org/stable/modules/generated/sklearn.ensemble.ExtraTreesClassifier.html

Iremos construir um classificador ExtraTreesClassifier.

In [9]:
# Criação do modelo para seleção de atributos
modelo = ExtraTreesClassifier(n_estimators=100)
modelo.fit(X,y)

# Exibe os atributos orginais
print("\nAtributos Originais:", vinhoreddados.columns[0:8])

# Exibe a pontuação de importância para cada atributo (quanto maior a pontuação, mais importante é o atributo). 
# Atributos selecionados: plas, mass, pedi, age.
print(modelo.feature_importances_)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-9-609783d10c8b> in <module>
      1 # Criação do modelo para seleção de atributos
      2 modelo = ExtraTreesClassifier(n_estimators=100)
----> 3 modelo.fit(X,y)
      4 
      5 # Exibe os atributos orginais

/usr/local/lib/python3.8/dist-packages/sklearn/ensemble/_forest.py in fit(self, X, y, sample_weight)
    365         self.n_outputs_ = y.shape[1]
    366 
--> 367         y, expanded_class_weight = self._validate_y_class_weight(y)
    368 
    369         if getattr(y, "dtype", None) != DOUBLE or not y.flags.contiguous:

/usr/local/lib/python3.8/dist-packages/sklearn/ensemble/_forest.py in _validate_y_class_weight(self, y)
    732 
    733     def _validate_y_class_weight(self, y):
--> 734         check_classification_targets(y)
    735 
    736         y = np.copy(y)

/usr/local/lib/python3.8/dist-packages/sklearn/utils/multiclass.py in check_classification_targets(y)
    195         "multilabel-sequences",
    196     ]:
--> 197         raise ValueError("Unknown label type: %r" % y_type)
    198 
    199 

ValueError: Unknown label type: 'continuous'

Resumo:¶

  • Técnica 1: preg, plas, mass e age
  • Técnica 2: preg, plas, mass e pedi
  • Técnica 3: plas, mass, pedi, age

Curiosidade: no próximo notebook, após tratarmos os missings, ficaremos com as colunas: preg, plas, pres, mass, pedi, age - a única que não foi selecionada por algum método de feature selection foi pres

OBS: É possível utilizar técnicas de Feature selection como parte de um pipeline. Veja um exemplo no trecho de código a seguir:

In [10]:
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif
from sklearn.linear_model import LogisticRegression 
from sklearn.pipeline import Pipeline 

pipeline = Pipeline([
  ('feature_selection', SelectKBest(score_func=f_classif, k=4)),
  ('classification', RandomForestClassifier(n_estimators=100, max_features=3, random_state=7))
])
In [11]:
print (pipeline)
Pipeline(steps=[('feature_selection', SelectKBest(k=4)),
                ('classification',
                 RandomForestClassifier(max_features=3, random_state=7))])