Pandas: Liste de souhaits: rendre get_dummies () utilisable pour le cadre de train / test

Créé le 28 nov. 2014  ·  21Commentaires  ·  Source: pandas-dev/pandas

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).

Categorical Reshaping Usage Question

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 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.

Tous les 21 commentaires

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.

Exemple des limitations de l'actuel get dummies

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 verte

Dans [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 .

@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

importer des données de SQL vers pandas Dataframe

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

* * * * * ")
imprimer (InputDataSet)

print (" * Informations sur les colonnes de la catégorie

* * ")
colonnes = ['order_source_desc']
InputDataSet [colonnes] = InputDataSet [colonnes] .apply (lambda x: x.astype ('category'))

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)

pluie = transformation enc (X)

Prod = transformée en enc (A)

imprimer (Prod)

SORTIE: * * * * Données

* * * *
order_key service_date_key order_source_desc return_flag
0 10087937 20151214 En ligne 1
1 10088174 20151201 Appel entrant 2
2 10088553 20151217 Appel entrant 2
3 663478 20160806 Appel sortant 1
* Informations sur les colonnes de la catégorie * *

RangeIndex: 4 entrées, 0 à 3
Colonnes de données (4 colonnes au total):
order_key 4 non nul int64
service_date_key 4 non nul int64
order_source_desc 4 catégorie non nulle
return_flag 4 non nul int64
dtypes: catégorie (1), int64 (3)
utilisation de la mémoire: 284,0 octets
* Régression linéaire * * *
[[10087937 20151214 1]]

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

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

michaelaye picture michaelaye  ·  64Commentaires

jorisvandenbossche picture jorisvandenbossche  ·  50Commentaires

rvernica picture rvernica  ·  46Commentaires

datapythonista picture datapythonista  ·  44Commentaires

bgrayburn picture bgrayburn  ·  46Commentaires
bleepcoder.com utilise des informations sous licence publique GitHub pour fournir aux développeurs du monde entier des solutions à leurs problèmes. Nous ne sommes pas affiliés à GitHub, Inc. ni à aucun développeur qui utilise GitHub pour ses projets. Nous n'hébergeons aucune des vidéos ou images sur nos serveurs. Tous les droits appartiennent à leurs propriétaires respectifs.
Source pour cette page: Source

Langages de programmation populaires
Projets GitHub populaires
Plus de projets GitHub

© 2024 bleepcoder.com - Contact
Made with in the Dominican Republic.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.