J'ai un problème de classification d'apprentissage automatique avec 80% de variables catégoriques. Dois-je utiliser un codage à chaud si je veux utiliser un classificateur pour la classification? Puis-je transmettre les données à un classificateur sans le codage?
J'essaie de procéder comme suit pour la sélection des fonctionnalités:
J'ai lu le dossier du train:
num_rows_to_read = 10000
train_small = pd.read_csv("../../dataset/train.csv", nrows=num_rows_to_read)
Je change le type de caractéristiques catégorielles en 'catégorie':
non_categorial_features = ['orig_destination_distance',
'srch_adults_cnt',
'srch_children_cnt',
'srch_rm_cnt',
'cnt']
for categorical_feature in list(train_small.columns):
if categorical_feature not in non_categorial_features:
train_small[categorical_feature] = train_small[categorical_feature].astype('category')
J'utilise un encodage à chaud:
train_small_with_dummies = pd.get_dummies(train_small, sparse=True)
Le problème est que la 3ème partie reste souvent bloquée, bien que j'utilise une machine puissante.
Ainsi, sans le codage à chaud, je ne peux faire aucune sélection de fonctionnalités pour déterminer l’importance des fonctionnalités.
Que recommandez-vous?
Approche 1: Vous pouvez utiliser get_dummies sur pandas dataframe.
Exemple 1:
import pandas as pd
s = pd.Series(list('abca'))
pd.get_dummies(s)
Out[]:
a b c
0 1.0 0.0 0.0
1 0.0 1.0 0.0
2 0.0 0.0 1.0
3 1.0 0.0 0.0
Exemple 2:
Ce qui suit va transformer une colonne donnée en une liste chaude. Utilisez le préfixe pour avoir plusieurs nuls.
import pandas as pd
df = pd.DataFrame({
'A':['a','b','a'],
'B':['b','a','c']
})
df
Out[]:
A B
0 a b
1 b a
2 a c
# Get one hot encoding of columns B
one_hot = pd.get_dummies(df['B'])
# Drop column B as it is now encoded
df = df.drop('B',axis = 1)
# Join the encoded df
df = df.join(one_hot)
df
Out[]:
A a b c
0 a 0 1 0
1 b 1 0 0
2 a 0 0 1
Approche 2: Utilisez Scikit-learn
Étant donné un ensemble de données comportant trois entités et quatre exemples, nous permettons au codeur de rechercher la valeur maximale par caractéristique et de transformer les données en un codage binaire à une étape à chaud.
>>> from sklearn.preprocessing import OneHotEncoder
>>> enc = OneHotEncoder()
>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])
OneHotEncoder(categorical_features='all', dtype=<class 'numpy.float64'>,
handle_unknown='error', n_values='auto', sparse=True)
>>> enc.n_values_
array([2, 3, 4])
>>> enc.feature_indices_
array([0, 2, 5, 9], dtype=int32)
>>> enc.transform([[0, 1, 1]]).toarray()
array([[ 1., 0., 0., 1., 0., 0., 1., 0., 0.]])
Voici le lien pour cet exemple: http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
Vous pouvez le faire avec numpy.eye
et en utilisant le mécanisme de sélection d'élément de tableau:
_import numpy as np
nb_classes = 6
data = [[2, 3, 4, 0]]
def indices_to_one_hot(data, nb_classes):
"""Convert an iterable of indices to one-hot encoded labels."""
targets = np.array(data).reshape(-1)
return np.eye(nb_classes)[targets]
_
La valeur de retour de indices_to_one_hot(nb_classes, data)
est maintenant
_array([[[ 0., 0., 1., 0., 0., 0.],
[ 0., 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 1., 0.],
[ 1., 0., 0., 0., 0., 0.]]])
_
La .reshape(-1)
est là pour vous assurer que vous avez le bon format d'étiquettes (vous pourriez aussi avoir _[[2], [3], [4], [0]]
_).
Premièrement, le moyen le plus simple de coder à chaud est d'utiliser Sklearn.
http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
Deuxièmement, je ne pense pas que l’utilisation de pandas pour un code chaud soit aussi simple que cela (pas encore confirmé)
Création de variables nominales dans pandas pour python
Enfin, vous est-il nécessaire de faire un encodage à chaud? Un codage à chaud augmente de manière exponentielle le nombre de fonctionnalités, augmentant considérablement le temps d'exécution de tout classificateur ou de tout autre élément que vous allez exécuter. Surtout quand chaque caractéristique catégorique a plusieurs niveaux. Au lieu de cela, vous pouvez effectuer un codage factice.
L'utilisation de l'encodage factice fonctionne généralement bien, avec beaucoup moins de temps d'exécution et de complexité. Un prof sage m'a dit un jour: "Moins, c'est plus".
Voici le code de ma fonction de codage personnalisé si vous le souhaitez.
from sklearn.preprocessing import LabelEncoder
#Auto encodes any dataframe column of type category or object.
def dummyEncode(df):
columnsToEncode = list(df.select_dtypes(include=['category','object']))
le = LabelEncoder()
for feature in columnsToEncode:
try:
df[feature] = le.fit_transform(df[feature])
except:
print('Error encoding '+feature)
return df
EDIT: La comparaison pour être plus clair:
One-hot encoding: convertit n niveaux en n-1 colonnes.
Index Animal Index cat mouse
1 dog 1 0 0
2 cat --> 2 1 0
3 mouse 3 0 1
Vous pouvez voir comment cela va exploser votre mémoire si vous avez plusieurs types (ou niveaux) différents dans votre fonction catégorique. Gardez à l'esprit, il ne s'agit que d'une colonne.
Codage factice:
Index Animal Index Animal
1 dog 1 0
2 cat --> 2 1
3 mouse 3 2
Convertissez plutôt en représentations numériques. Gain de place pour les fonctionnalités, au prix d'un peu de précision.
Un encodage à chaud avec pandas est très simple:
def one_hot(df, cols):
"""
@param df pandas DataFrame
@param cols a list of columns to encode
@return a DataFrame with one-hot encoding
"""
for each in cols:
dummies = pd.get_dummies(df[each], prefix=each, drop_first=False)
df = pd.concat([df, dummies], axis=1)
return df
MODIFIER:
Une autre façon de one_hot en utilisant LabelBinarizer
de sklearn:
from sklearn.preprocessing import LabelBinarizer
label_binarizer = LabelBinarizer()
label_binarizer.fit(all_your_labels_list) # need to be global or remembered to use it later
def one_hot_encode(x):
"""
One hot encode a list of sample labels. Return a one-hot encoded vector for each label.
: x: List of sample Labels
: return: Numpy array of one-hot encoded labels
"""
return label_binarizer.transform(x)
Vous pouvez utiliser la fonction numpy.eye.
import numpy as np
def one_hot_encode(x, n_classes):
"""
One hot encode a list of sample labels. Return a one-hot encoded vector for each label.
: x: List of sample Labels
: return: Numpy array of one-hot encoded labels
"""
return np.eye(n_classes)[x]
def main():
list = [0,1,2,3,4,3,2,1,0]
n_classes = 5
one_hot_list = one_hot_encode(list, n_classes)
print(one_hot_list)
if __== "__main__":
main()
Résultat
D:\Desktop>python test.py
[[ 1. 0. 0. 0. 0.]
[ 0. 1. 0. 0. 0.]
[ 0. 0. 1. 0. 0.]
[ 0. 0. 0. 1. 0.]
[ 0. 0. 0. 0. 1.]
[ 0. 0. 0. 1. 0.]
[ 0. 0. 1. 0. 0.]
[ 0. 1. 0. 0. 0.]
[ 1. 0. 0. 0. 0.]]
Beaucoup plus facile à utiliser Pandas pour un encodage simple à chaud. Si vous cherchez plus d'options, vous pouvez utiliser scikit-learn.
Pour un encodage one-hot de base avec Pandas , vous passez simplement votre trame de données dans le get_dummies une fonction.
Par exemple, si j'ai un cadre de données appelé imdb_movies :
... et je veux encoder à chaud la colonne Rated, je fais simplement ceci:
pd.get_dummies(imdb_movies.Rated)
Cela renvoie une nouvelle image de données avec une colonne pour chaque niveau de notation existant ( ", ainsi qu'un 1 ou un 0 spécifiant la présence de cette évaluation pour une observation donnée.
Habituellement, nous souhaitons que cela fasse partie de la trame de données d'origine. Dans ce cas, nous attachons simplement notre nouveau cadre codé factice sur le cadre d'origine en utilisant " liaison de colonne .
Nous pouvons relier les colonnes en utilisant la fonction Pandas concat :
rated_dummies = pd.get_dummies(imdb_movies.Rated)
pd.concat([imdb_movies, rated_dummies], axis=1)
Nous pouvons maintenant exécuter une analyse sur notre dataframe complet.
FONCTION D'UTILITE SIMPLE
Je recommanderais de vous faire une fonction utilitaire pour le faire rapidement:
def encode_and_bind(original_dataframe, feature_to_encode):
dummies = pd.get_dummies(original_dataframe[[feature_to_encode]])
res = pd.concat([original_dataframe, dummies], axis=1)
return(res)
Utilisation :
encode_and_bind(imdb_movies, 'Rated')
Résultat :
De même, conformément au commentaire de @pmalbu, si vous souhaitez que la fonction supprime la fonctionnalité originale_to_encode , utilisez cette version:
def encode_and_bind(original_dataframe, feature_to_encode):
dummies = pd.get_dummies(original_dataframe[[feature_to_encode]])
res = pd.concat([original_dataframe, dummies], axis=1)
res = res.drop([feature_to_encode], axis=1)
return(res)
les pandas ont la fonction "get_dummies" incorporée pour obtenir un encodage à chaud de cette colonne/s.
code à une ligne pour l'encodage à chaud:
df=pd.concat([df,pd.get_dummies(df['column name'],prefix='column name')],axis=1).drop(['column name'],axis=1)
Le codage à une étape nécessite un peu plus que la conversion des valeurs en variables indicatrices. En règle générale, le processus ML exige que vous appliquiez ce codage plusieurs fois à des ensembles de données de validation ou de test et que vous appliquiez le modèle que vous construisez à des données observées en temps réel. Vous devez stocker le mappage (transformation) utilisé pour construire le modèle. Une bonne solution utiliserait le DictVectorizer
ou LabelEncoder
(suivi de get_dummies
. Voici une fonction que vous pouvez utiliser:
def oneHotEncode2(df, le_dict = {}):
if not le_dict:
columnsToEncode = list(df.select_dtypes(include=['category','object']))
train = True;
else:
columnsToEncode = le_dict.keys()
train = False;
for feature in columnsToEncode:
if train:
le_dict[feature] = LabelEncoder()
try:
if train:
df[feature] = le_dict[feature].fit_transform(df[feature])
else:
df[feature] = le_dict[feature].transform(df[feature])
df = pd.concat([df,
pd.get_dummies(df[feature]).rename(columns=lambda x: feature + '_' + str(x))], axis=1)
df = df.drop(feature, axis=1)
except:
print('Error encoding '+feature)
#df[feature] = df[feature].convert_objects(convert_numeric='force')
df[feature] = df[feature].apply(pd.to_numeric, errors='coerce')
return (df, le_dict)
Cela fonctionne sur un pandas dataframe et pour chaque colonne du dataframe, il crée et retourne un mappage. Donc, vous appelleriez ça comme ça:
train_data, le_dict = oneHotEncode2(train_data)
Ensuite, sur les données de test, l’appel est effectué en passant le dictionnaire renvoyé de la formation:
test_data, _ = oneHotEncode2(test_data, le_dict)
Une méthode équivalente consiste à utiliser DictVectorizer
. Un article connexe sur le même est sur mon blog. Je le mentionne ici car il fournit un raisonnement derrière cette approche par rapport à simplement utiliser get_dummies post (divulgation: ceci est mon propre blog).
Voici une solution utilisant DictVectorizer
et la méthode Pandas DataFrame.to_dict('records')
.
>>> import pandas as pd
>>> X = pd.DataFrame({'income': [100000,110000,90000,30000,14000,50000],
'country':['US', 'CAN', 'US', 'CAN', 'MEX', 'US'],
'race':['White', 'Black', 'Latino', 'White', 'White', 'Black']
})
>>> from sklearn.feature_extraction import DictVectorizer
>>> v = DictVectorizer()
>>> qualitative_features = ['country','race']
>>> X_qual = v.fit_transform(X[qualitative_features].to_dict('records'))
>>> v.vocabulary_
{'country=CAN': 0,
'country=MEX': 1,
'country=US': 2,
'race=Black': 3,
'race=Latino': 4,
'race=White': 5}
>>> X_qual.toarray()
array([[ 0., 0., 1., 0., 0., 1.],
[ 1., 0., 0., 1., 0., 0.],
[ 0., 0., 1., 0., 1., 0.],
[ 1., 0., 0., 0., 0., 1.],
[ 0., 1., 0., 0., 0., 1.],
[ 0., 0., 1., 1., 0., 0.]])
Je sais que je suis en retard pour cette soirée, mais le moyen le plus simple de coder à chaud une trame de données de manière automatisée consiste à utiliser cette fonction:
def hot_encode(df):
obj_df = df.select_dtypes(include=['object'])
return pd.get_dummies(df, columns=obj_df.columns).values
Vous pouvez transmettre les données au classifieur catboost sans codage. Catboost gère lui-même les variables catégoriques en effectuant un codage moyen à expansion unique et ciblée.
Pour ajouter à d’autres questions, laissez-moi vous expliquer comment je l’ai fait avec une fonction Python 2.0 à l’aide de Numpy:
def one_hot(y_):
# Function to encode output labels from number indexes
# e.g.: [[5], [0], [3]] --> [[0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0]]
y_ = y_.reshape(len(y_))
n_values = np.max(y_) + 1
return np.eye(n_values)[np.array(y_, dtype=np.int32)] # Returns FLOATS
La ligne n_values = np.max(y_) + 1
pourrait être codée en dur pour que vous utilisiez le bon nombre de neurones au cas où vous utiliseriez des mini-lots, par exemple.
Projet/tutoriel de démonstration où cette fonction a été utilisée: https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition
Je l'ai utilisé dans mon modèle acoustique: cela aide probablement dans votre modèle.
def one_hot_encoding(x, n_out):
x = x.astype(int)
shape = x.shape
x = x.flatten()
N = len(x)
x_categ = np.zeros((N,n_out))
x_categ[np.arange(N), x] = 1
return x_categ.reshape((shape)+(n_out,))
Cela fonctionne pour moi:
pandas.factorize( ['B', 'C', 'D', 'B'] )[0]
Sortie:
[0, 1, 2, 0]
Vous pouvez également procéder comme suit. Notez pour le dessous que vous n'avez pas besoin d'utiliser pd.concat
.
import pandas as pd
# intialise data of lists.
data = {'Color':['Red', 'Yellow', 'Red', 'Yellow'], 'Length':[20.1, 21.1, 19.1, 18.1],
'Group':[1,2,1,2]}
# Create DataFrame
df = pd.DataFrame(data)
for _c in df.select_dtypes(include=['object']).columns:
print(_c)
df[_c] = pd.Categorical(df[_c])
df_transformed = pd.get_dummies(df)
df_transformed
Vous pouvez également modifier les colonnes explicites en catégories. Par exemple, ici je change les variables Color
et Group
import pandas as pd
# intialise data of lists.
data = {'Color':['Red', 'Yellow', 'Red', 'Yellow'], 'Length':[20.1, 21.1, 19.1, 18.1],
'Group':[1,2,1,2]}
# Create DataFrame
df = pd.DataFrame(data)
columns_to_change = list(df.select_dtypes(include=['object']).columns)
columns_to_change.append('Group')
for _c in columns_to_change:
print(_c)
df[_c] = pd.Categorical(df[_c])
df_transformed = pd.get_dummies(df)
df_transformed
Cela peut et devrait être simple comme:
class OneHotEncoder:
def __init__(self,optionKeys):
length=len(optionKeys)
self.__dict__={optionKeys[j]:[0 if i!=j else 1 for i in range(length)] for j in range(length)}
Utilisation:
ohe=OneHotEncoder(["A","B","C","D"])
print(ohe.A)
print(ohe.D)