Avoir get_dummies () dans Pandas est vraiment bien, mais pour être utile pour l'apprentissage automatique, il faudrait qu'il soit utilisable dans un cadre de train / test (ou "fit_transform" et "transform", avec la terminologie sklearn). Faites-moi savoir si cela nécessite plus d'explications.
Donc, je suppose que c'est un rapport de bogue de liste de souhaits pour ajouter cette fonctionnalité à Pandas. Je peux même créer une pull request, si les gens conviennent que ce serait quelque chose d'utile à avoir dans Pandas (et sont prêts à coacher un peu et à faire une révision de code pour ce qui serait ma première contribution à ce projet).
Eh bien, que diriez-vous d'un exemple de pseudo-code avec des entrées et des sorties d'un cadre d'échantillon serait utile
@ chrish42 , un exemple serait génial.
FYI scikit-learn a la classe OneHotEncoder qui s'intègre dans leur pipeline.
Quelque chose comme ça devrait fonctionner?
import pandas as pd
from sklearn.pipeline import TransformerMixin
class DummyEncoder(TransformerMixin):
def __init__(self, columns=None):
self.columns = columns
def transform(self, X, y=None, **kwargs):
return pd.get_dummies(X, columns=self.columns)
def fit(self, X, y=None, **kwargs):
return self
Donnant
In [15]: df
Out[15]:
A B C
0 1 a a
1 2 b a
In [16]: DummyEncoder().transform(df)
Out[16]:
A B_a B_b C_a
0 1 1 0 1
1 2 0 1 1
Soyez prudent avec l'ordre des colonnes.
@TomAugspurger , en fait la compatibilité avec le pipeline de traitement sklearn lui-même n'est pas la partie qui m'intéresse. Ce que je voudrais, c'est la possibilité de sauvegarder la transformation effectuée par get_dummes () dans un ensemble de données, puis d'appliquer ladite transformation telle quelle (en créant exactement les mêmes colonnes), même si le deuxième ensemble de données a un sous-ensemble des valeurs du premier dans une colonne, etc. C'est en fait ce que je voulais dire par "utilisable dans un cadre de train / test". Cette explication est-elle plus claire? (Je peux ajouter un exemple que quelqu'un pense qu'il est encore nécessaire.)
Je connais la classe OneHotEncoder
dans sklearn, mais elle a d'autres limitations.
J'ai trébuché sur le même problème que @ chrish42 et j'ai trouvé que get_dummies me faisait mal à la tête.
Supposons que nous travaillons avec les données du DataFrame df_train suivant
`` .python
df_train = pandas.DataFrame ({"voiture": ["siège", "bmw"], "couleur": ["rouge", "vert"]})
pandas.get_dummies (df_train)
car_bmw car_seat color_green color_red
0 0 1 0 1
1 1 0 1 0
Then we are provided with
``` .python
df_test = pandas.DataFrame({"car":["seat","mercedes"], "color":["red","green"]})
pandas.get_dummies(df_test )
car_mercedes car_seat color_green color_red
0 0 1 0 1
1 1 0 1 0
Comme je n'ai jamais observé de valeur "mercedes" pour la variable "voiture" dans df_train, j'aimerais pouvoir obtenir le codage à chaud suivant:
`` .python
car_bmw car_seat color_green color_red
0 0 1 0 1
1 0 0 1 0
Where the column car_mercedes actually never appears.
This could be solved by allowing get_dummies to receive an input dictionary stating the accepted values that we allow for each column.
Returning to the previous example, we could give as input to get_dummies the following dict of sets
``` .python
accepted_values_per_column = {'car': {'bmw', 'seat'}, 'color': {'green', 'red'}}
et nous nous attendrions à ce que get_dummies revienne
`` .python
get_dummies (df_test, acceptées_values_per_column = acceptées_values_per_column)
car_bmw car_seat color_green color_red
0 0 1 0 1
1 0 0 1 0
''
et attendez que get_dummies (df_test) renvoie ce qui retourne déjà.
Vous devez simplement créer vos variables Categorical
si vous voulez spécifier des variables _possiblement_ non observées. Cela peut être fait au moment de la création ou après, voir la documentation
In [5]: df_train = pd.DataFrame({"car":Series(["seat","bmw"]).astype('category',categories=['seat','bmw','mercedes']),"color":["red","green"]})
In [6]: df_train
Out[6]:
car color
0 seat red
1 bmw green
In [7]: pd.get_dummies(df_train )
Out[7]:
car_seat car_bmw car_mercedes color_green color_red
0 1 0 0 0 1
1 0 1 0 1 0
La question d'origine n'est pas bien spécifiée, donc fermeture.
Et quand vous allez dans l'autre sens, de l'encodage au retour à Catégorique, vous utiliserez Categorical.from_codes.
Encore un petit conseil non sollicité. Si vous vous souciez des estimations précises des coefficients sur les catégories, supprimez l'une des colonnes encodées ou bien vous aurez une multicolinéarité avec l'interception (si vous en avez une).
Le 5 octobre 2015, à 05:34, Jeff Reback [email protected] a écrit:
Vous devez simplement rendre vos variables catégoriques si vous souhaitez spécifier des variables éventuellement non observées. Cela peut être fait au moment de la création ou après, voir la documentation
Dans [5]: df_train = pd.DataFrame ({"car": Series (["seat", "bmw"]). Astype ('category', categories = ['seat', 'bmw', 'mercedes'] ), "couleur": ["rouge", "vert"]})
Dans [6]: df_train
Sortie [6]:
couleur de la voiture
0 siège rouge
1 bmw verteDans [7]: pd.get_dummies (df_train)
Sortie [7]:
car_seat car_bmw car_mercedes color_green color_red
0 1 0 0 0 1
1 0 1 0 1 0
La question d'origine n'est pas bien spécifiée, donc fermeture.-
Répondez directement à cet e-mail ou affichez-le sur GitHub.
@TomAugspurger @jreback Je pense que j'ai rencontré le même problème ces derniers temps et je voudrais donner un exemple
train_a = pd.DataFrame ({"IsBadBuy": [0,1,0], "Make": ['Toyota', 'Mazda', 'BMW']})
IsBadBuy Make_BMW Make_Mazda Make_Toyota
0 0 0 0 1
1 1 0 1 0
2 0 1 0 0
test_a = pd.DataFrame ({"Marque": ['Toyota', 'BMW']})
print pd.get_dummies (test_a, colonnes = ['Make'])
Make_BMW Make_Toyota
0 0 1
1 1 0
Ici, idéalement, la colonne Make_Mazda devrait être préservée car l'algorithme ML s'attendrait au même nombre de fonctionnalités et les valeurs que nous obtiendrons dans le test seront un sous-ensemble de celles en cours.
Utilisez un catégorique. Cela s'étendra au nombre correct de colonnes. J'ai donné une conférence à ce sujet si vous êtes intéressé https://m.youtube.com/watch?v=KLPtEBokqQ0
_____________________________
De: Ajay Saxena [email protected]
Envoyé: jeudi 12 janvier 2017 18:31
Sujet: Re: [pandas-dev / pandas] Liste de souhaits: rendre get_dummies () utilisable pour le framework train / test (# 8918)
À: pandas-dev / pandas [email protected]
Cc: Tom Augspurger [email protected] , Mention [email protected]
@jreback Je pense avoir rencontré le même problème ces derniers temps et je voudrais donner un exemple
train_a = pd.DataFrame ({"IsBadBuy": [0,1,0], "Make": ['Toyota', 'Mazda', 'BMW']})
IsBadBuy Make_BMW Make_Mazda Make_Toyota
0 0 0 0 1
1 1 0 1 0
2 0 1 0 0
test_a = pd.DataFrame ({"Marque": ['Toyota', 'BMW']})
print pd.get_dummies (test_a, colonnes = ['Make'])
Make_BMW Make_Toyota
0 0 1
1 1 0
Ici, idéalement, la colonne Make_Mazda devrait être préservée car l'algorithme ML s'attendrait au même nombre de fonctionnalités et les valeurs que nous obtiendrons dans le test seront un sous-ensemble de celles en cours.
-
Vous recevez cela parce que vous avez été mentionné.
Répondez directement à cet e-mail, affichez-le sur GitHub ou désactivez le fil de discussion.
Merci @TomAugspurger
La conférence PyData Chicago 2016 donnée par @TomAugspurger était vraiment bien faite. Il a fait un travail fantastique en illustrant toutes les raisons pour lesquelles ce problème / demande ne devrait pas être fermé. IMHO soit sa classe DummyEncoder ou un équivalent raisonnable devrait être inclus dans Pandas proprement dit. Oui, je peux aller sur son github et copier / émuler sa classe, mais ce serait beaucoup plus agréable de la prendre en charge dans la bibliothèque.
Je pense qu'il y a un besoin pour une bibliothèque qui commence tôt dans la modélisation des données
pipeline et fonctionne bien avec les pandas et scikit-learn.
Mais les pandas ne dépendent pas de scikit-learn et vice-versa. Je pense qu'il y a
place pour une autre bibliothèque construite sur les deux.
Le mercredi 10 mai 2017 à 18h13, Brian Wylie [email protected]
a écrit:
La conférence PyData Chicago 2016 donnée par @TomAugspurger
https://github.com/TomAugspurger était vraiment bien fait. Il a fait un
travail fantastique d'illustrer toutes les raisons pour lesquelles ce problème / demande devrait
ne pas être fermé. IMHO soit sa classe DummyEncoder ou un raisonnable
l'équivalent doit être inclus dans Pandas proprement dit. Oui je peux aller sur son github
et copiez / émulez sa classe mais ce serait beaucoup plus agréable de l'avoir
pris en charge au sein de la bibliothèque.BTW je pense que @TomAugspurger https://github.com/TomAugspurger pourrait être mon
nouveau gourou PyData préféré. Je vais traquer tout ce qu'il est
fait / travaille et essayez de l'absorber .. pas de manière effrayante / traque .. vous
savoir juste d'une manière normale qui n'est pas du tout effrayante. :)-
Vous recevez cela parce que vous avez été mentionné.
Répondez directement à cet e-mail, affichez-le sur GitHub
https://github.com/pandas-dev/pandas/issues/8918#issuecomment-300638388 ,
ou couper le fil
https://github.com/notifications/unsubscribe-auth/ABQHIpTqgHSE7iFVF9Pp4_YoKB9DPLcEks5r4kSrgaJpZM4DB6Hb
.
Voici une petite solution sur laquelle certains d'entre nous ont travaillé qui peut être utile pour certains ici. Variables factices avec capacités d'ajustement / transformation.
https://github.com/joeddav/get_smarties
Des commentaires et des contributions seraient utiles!
Cela semble lié à # 14017
J'ai créé une solution qui peut être utile exactement dans ce problème. Une variable catégorielle de codage à chaud dans un cadre de test de train. Il peut également gérer les cas où le jeu de données est trop volumineux pour tenir dans la mémoire de la machine.
https://github.com/yashu-seth/dummyPy
Vous pouvez également trouver un petit tutoriel à ce sujet ici .
Les personnes qui y sont abonnées peuvent être intéressées par l'implémentation de dask-ml
@TomAugspurger Ce code ne fonctionne pas. Lorsque je vais transformer mes données d'enregistrement unique de production, cela ne me donne que la colonne encodée à chaud pour la valeur unique présente.
Qu'est-ce que je rate?
importer pyodbc
importer des cornichons
depuis sklearn.linear_model import LogisticRegression
depuis sklearn.linear_model import LinearRegression
importer numpy comme np
importer des pandas en tant que pd
depuis sklearn.pipeline import TransformerMixin
depuis sklearn.pipeline import make_pipeline
classe DummyEncoder (TransformerMixin):
def fit (self, X, y = None):
self.index_ = X.index
self.columns_ = X.columns
self.cat_columns_ = X.select_dtypes (include = ['category']). colonnes
self.non_cat_columns_ = X.columns.drop (self.cat_columns_)
self.cat_map_ = {col: X[col].cat for col in self.cat_columns_}
left = len(self.non_cat_columns_)
self.cat_blocks_ = {}
for col in self.cat_columns_:
right = left + len(X[col].cat.categories)
self.cat_blocks_[col], left = slice(left, right), right
return self
def transform(self, X, y=None):
return np.asarray(pd.get_dummies(X))
def inverse_transform(self, X):
non_cat = pd.DataFrame(X[:, :len(self.non_Cat_columns_)],
columns=self.non_cat_columns_)
cats = []
for col, cat in self.cat_map_.items():
slice_ = self.cat_blocks_[col]
codes = X[:, slice_].argmax(1)
series = pd.Series(pd.Categorical.from_codes(
codes, cat.categories, ordered=cat.ordered
), name=col)
cats.append(series)
df = pd.concat([non_cat] + cats, axis=1)[self.columns_]
return df
cnxn = pyodbc.connect ('DRIVER = {SQL Server}; SERVER = {XXXXX}; DATABASE = {ML_Learn_Taxi}; UID = {XXXX}; PWD = {XXXX}')
sql = "" "
SELECT top 1 CONVERT (int, [order_key]) order_key
, CONVERT (int, [service_date_key]) service_date_key
, [order_source_desc]
, 1 comme 'return_flag'
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'En ligne'
SYNDICAT
SELECT 2 top CONVERT (int, [order_key])
, CONVERT (int, [clé_date_service])
, [order_source_desc]
, 2
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'Appel entrant'
SYNDICAT
SELECT top 1 CONVERT (int, [order_key])
, CONVERT (int, [clé_date_service])
, [order_source_desc]
,1
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'Appel sortant'
"" "
prod_sql = "" "
SELECT top 1 CONVERT (int, [order_key]) order_key
, CONVERT (int, [service_date_key]) service_date_key
, [order_source_desc]
, 1 comme 'return_flag'
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'En ligne'
"" "
InputDataSet = pd.read_sql (sql, cnxn)
ProdDataSet = pd.read_sql (prod_sql, cnxn)
print (" * * * * Données
* * * * * ")print (" * Informations sur les colonnes de la catégorie
* * ")InputDataSet.info ()
print (" * Régression linéaire
* * ")X = InputDataSet.drop ('drapeau_retour', axe = 1)
y = InputDataSet ['return_flag']
A = ProdDataSet.drop ('drapeau_retour', axe = 1)
B = ProdDataSet ['return_flag']
enc = DummyEncoder ()
enc.fit (X)
Prod = transformée en enc (A)
imprimer (Prod)
SORTIE: * * * * Données
* * * *Je pense donc que ce fil est un peu compliqué, je vais donc essayer de résumer une solution simple ici et comment cela est déjà possible. Je vais démontrer sur une colonne, mais vous pouvez le généraliser à plusieurs.
Donc, dans un appel "en forme", faites simplement:
categories = sorted(training_data.iloc[:, column_index].value_counts(dropna=True).index)
Vous stockez categories
dans l'état que vous apprenez lors de l'appareillage.
Et puis dans "transformer" vous faites:
from pandas.api import types as pandas_types
categorical_data = testing_data.iloc[:, [column_index]].astype(
pandas_types.CategoricalDtype(categories=categories),
)
one_hot_encoded = pandas.get_dummies(categorical_data)
Et il effectuera un encodage à chaud toujours dans le même mappage pour les valeurs. Si une valeur catégorielle n'était pas présente pendant la formation, elle sera considérée comme NaN pendant le test. Si une valeur n'est pas vue pendant le test, aucune colonne ne sera définie pour elle.
C'est très gentil. Je souhaite juste que tous ceux qui veulent faire cela n'aient pas à le découvrir à nouveau. ;-)
L'approche suggérée par @mitar est un bel exemple court. Pour une exploration plus longue de ce problème, voici un cahier qui pourrait être utile / utile: https://nbviewer.jupyter.org/github/SuperCowPowers/scp-labs/blob/master/notebooks/Categorical_Encoding_Dangers.ipynb
Vu le code ci-dessous dans l'exercice du didacticiel Kaggle XGBoost. Cela fait l'affaire.
X_train = pd.get_dummies(X_train)
X_valid = pd.get_dummies(X_valid)
X_test = pd.get_dummies(X_test)
X_train, X_valid = X_train.align(X_valid, join='left', axis=1)
X_train, X_test = X_train.align(X_test, join='left', axis=1)
J'ai également rencontré le même problème plusieurs fois. J'ai écrit un cours ci-dessous (en prenant des idées de cette discussion) qui m'a facilité les choses.
import pandas
from sklearn.preprocessing import LabelEncoder
class CategoryEncoder:
'''
labelEncoding : boolean -> True If the categorical columns are to be label encoded
oneHotEncoding : boolean -> True If the categorical columns are to be one hot encoded (using pandas.get_dummies method)
dropFirst : boolean -> True if first column is to be dropped (usually to avoid multi-collinearity) post one hot encoding
Doesn't matter if oneHotEncoding = False
df : pandas.DataFrame() -> dataframe object that needs to be encoded
catCols : list -> list of the categorical columns that need to be encoded
'''
def __init__(self,labelEncoding=True,oneHotEncoding=False,dropFirst=False):
self.labelEncoding = labelEncoding
self.oneHotEncoding = oneHotEncoding
self.dropFirst = dropFirst
self.labelEncoder = {}
self.oneHotEncoder = {}
def fit(self,df,catCols=[]):
df1 = df.copy()
if self.labelEncoding:
for col in catCols:
labelEncoder = LabelEncoder()
labelEncoder.fit(df1.loc[:,col].astype(str))
df1.loc[:,col] = labelEncoder.transform(df1.loc[:,col])
self.labelEncoder[col] = labelEncoder.classes_
if self.oneHotEncoding:
for col in catCols:
cats = sorted(df1.loc[:,col].value_counts(dropna=True).index)
self.oneHotEncoder[col] = cats
def transform(self,df,catCols=[]):
df1 = df.copy()
if self.labelEncoding:
for col in catCols:
labelEncoder = self.labelEncoder[col]
labelEncoder = {v:i for i,v in enumerate(labelEncoder.tolist())}
print(labelEncoder)
df1.loc[:,col] = df1.loc[:,col].map(labelEncoder)
if self.oneHotEncoding:
for col in catCols:
oneHotEncoder = self.oneHotEncoder[col]
df1.loc[:,col] = df1.loc[:,col].astype(pandas.CategoricalDtype(categories=oneHotEncoder))
df1 = pandas.get_dummies(df1,columns=catCols,drop_first=self.dropFirst)
return df1
Facile à lancer et à utiliser également une instance de l'encodeur.
enc1 = CategoryEncoder(True,False) # Will label encode but not one-hot encode
enc2 = CategoryEncoder(False,True,True) # Will one-hot encode but not label encode
enc3 = CategoryEncoder(True,True,True) # Will label encode first and then one-hot encode
# List of categorical columns you want to encode
categorical_columns = ['col_1', 'col_2']
enc1.fit(train_df, categorical_columns)
enc1.transform(test_df, categorical_columns) # Returns the dataframe encoded columns
REMARQUE: Cela ne prendra en charge aucune exception, par exemple en passant les noms de colonne qui ne sont pas disponibles dans le dataframe
Commentaire le plus utile
Vous devez simplement créer vos variables
Categorical
si vous voulez spécifier des variables _possiblement_ non observées. Cela peut être fait au moment de la création ou après, voir la documentationLa question d'origine n'est pas bien spécifiée, donc fermeture.