Ce notebook présente différentes options pour gérer les catégories au format entier ou texte.
from jyquickhelper import add_notebook_menu
add_notebook_menu()
%matplotlib inline
On construit un jeu très simple avec deux catégories, une entière, une au format texte.
import pandas
import numpy
df = pandas.DataFrame(dict(cat_int=[10, 20, 10, 39, 10, 10, numpy.nan],
cat_text=['catA', 'catB', 'catA', 'catDD', 'catB', numpy.nan, 'catB']))
df
cat_int | cat_text | |
---|---|---|
0 | 10.0 | catA |
1 | 20.0 | catB |
2 | 10.0 | catA |
3 | 39.0 | catDD |
4 | 10.0 | catB |
5 | 10.0 | NaN |
6 | NaN | catB |
Les premières opérations consiste à convertir une catégorie au format entier ou au format texte en un entier. Les valeurs manquantes ne sont toujours traitées de la même façon.
from sklearn.preprocessing import LabelEncoder
LabelEncoder().fit_transform(df['cat_int'])
array([0, 1, 0, 2, 0, 0, 3], dtype=int64)
try:
LabelEncoder().fit_transform(df['cat_text'])
except Exception as e:
print(e)
LabelEncoder().fit_transform(df['cat_text'].dropna())
'<' not supported between instances of 'float' and 'str'
array([0, 1, 0, 2, 1, 1], dtype=int64)
On peut récupérer l'association entre catégorie et catégorie codée.
le = LabelEncoder()
le.fit(df['cat_text'].dropna())
le.classes_
array(['catA', 'catB', 'catDD'], dtype=object)
La seconde opération permet de transformer une catégorie au format entier en plusieurs colonnes au format binaire.
from sklearn.preprocessing import OneHotEncoder
try:
OneHotEncoder().fit_transform(df[['cat_int']]).todense()
except Exception as e:
print(e)
OneHotEncoder().fit_transform(df[['cat_int']].dropna()).todense()
Input contains NaN, infinity or a value too large for dtype('float64').
matrix([[1., 0., 0.], [0., 1., 0.], [1., 0., 0.], [0., 0., 1.], [1., 0., 0.], [1., 0., 0.]])
from sklearn.preprocessing import LabelBinarizer
try:
LabelBinarizer().fit_transform(df[['cat_int']])
except Exception as e:
print(e)
LabelBinarizer().fit_transform(df[['cat_int']].dropna())
Unknown label type: ( cat_int 0 10.0 1 20.0 2 10.0 3 39.0 4 10.0 5 10.0 6 NaN,)
array([[1, 0, 0], [0, 1, 0], [1, 0, 0], [0, 0, 1], [1, 0, 0], [1, 0, 0]])
from sklearn.preprocessing import LabelBinarizer
try:
LabelBinarizer().fit_transform(df[['cat_text']])
except Exception as e:
print(e)
LabelBinarizer().fit_transform(df[['cat_text']].dropna())
'<' not supported between instances of 'float' and 'str'
array([[1, 0, 0], [0, 1, 0], [1, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 0]])
D'autres options qui ne fonctionnent pas tout à fait de la même manière en terme d'implémentation.
from sklearn.feature_extraction import DictVectorizer
DictVectorizer().fit_transform(df.to_dict('records')).todense()
matrix([[10., 0., 1., 0., 0.], [20., 0., 0., 1., 0.], [10., 0., 1., 0., 0.], [39., 0., 0., 0., 1.], [10., 0., 0., 1., 0.], [10., nan, 0., 0., 0.], [nan, 0., 0., 1., 0.]])
from sklearn.feature_extraction import FeatureHasher
FeatureHasher(n_features=5).fit_transform(df.to_dict('records')).todense()
matrix([[ 0., 0., 0., 1., 10.], [ 1., 0., 0., 0., 20.], [ 0., 0., 0., 1., 10.], [ 0., -1., 0., 0., 39.], [ 1., 0., 0., 0., 10.], [ 0., 0., 0., 0., nan], [ 1., 0., 0., 0., nan]])
On construit un simple jeu de données pour une régression linéaire, $Y = -10 X - 7$ puis on cale une régression linéaire avec $Y \sim \alpha X_2 + \epsilon$ où $X_2$ est une permutation de X. Le lien est en quelque sorte brisé.
perm = numpy.random.permutation(list(range(10)))
n = 1000
X1 = numpy.random.randint(0, 10, (n,1))
X2 = numpy.array([perm[i] for i in X1])
eps = numpy.random.random((n, 1))
Y = X1 * (-10) - 7 + eps
data = pandas.DataFrame(dict(X1=X1.ravel(), X2=X2.ravel(), Y=Y.ravel()))
data.head()
X1 | X2 | Y | |
---|---|---|---|
0 | 4 | 7 | -46.420964 |
1 | 6 | 9 | -66.321194 |
2 | 3 | 5 | -36.001053 |
3 | 0 | 2 | -6.802070 |
4 | 8 | 3 | -86.044988 |
from sklearn.model_selection import train_test_split
data_train, data_test = train_test_split(data)
data_train = data_train.copy()
data_test = data_test.copy()
On transforme la catégorie :
le = LabelEncoder().fit(data_train['X2'])
data_train['X3'] = le.transform(data_train['X2'])
data_test['X3'] = le.transform(data_test['X2'])
data_train.head()
X1 | X2 | Y | X3 | |
---|---|---|---|---|
943 | 1 | 8 | -16.020453 | 8 |
686 | 2 | 0 | -26.664813 | 0 |
312 | 9 | 6 | -96.833801 | 6 |
861 | 1 | 8 | -16.090882 | 8 |
784 | 4 | 7 | -46.693131 | 7 |
data_train.corr()
X1 | X2 | Y | X3 | |
---|---|---|---|---|
X1 | 1.000000 | 0.145122 | -0.999946 | 0.145122 |
X2 | 0.145122 | 1.000000 | -0.144419 | 1.000000 |
Y | -0.999946 | -0.144419 | 1.000000 | -0.144419 |
X3 | 0.145122 | 1.000000 | -0.144419 | 1.000000 |
On cale une régression linéaire de $Y$ sur $X_3$ la catégorie encodée :
from sklearn.linear_model import LinearRegression
clr = LinearRegression()
clr.fit(data_train[['X3']], data_train['Y'])
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
from sklearn.metrics import r2_score
r2_score(data_test['Y'], clr.predict(data_test[['X3']]))
0.009306055721419959
Autrement dit, elle n'a rien appris. On cale un arbre de décision :
from sklearn.tree import DecisionTreeRegressor
clr = DecisionTreeRegressor()
clr.fit(data_train[['X3']], data_train['Y'])
DecisionTreeRegressor(criterion='mse', max_depth=None, max_features=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, presort=False, random_state=None, splitter='best')
r2_score(data_test['Y'], clr.predict(data_test[['X3']]))
0.9998898230391275
L'arbre de décision a saisi la permutation alors que la régression linéaire n'a pas fonctionné. La régression linéaire n'est pas estimée à l'aide d'une méthode à base de gradient mais elle possède les mêmes contraintes, il est préférable que la cible Y soit une fonction le plus possible monotone de X. Avec une colonne par modalité de la catégorie, le résultat est tout autre.
one = OneHotEncoder().fit(data_train[['X2']])
feat = one.transform(data_train[['X2']])
feat[:5].todense()
matrix([[0., 0., 0., 0., 0., 0., 0., 0., 1., 0.], [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.], [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.], [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]])
clr = LinearRegression()
clr.fit(feat, data_train['Y'])
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
r2_score(data_test['Y'], clr.predict(one.transform(data_test[['X2']])))
0.9998898230391275