Reconstruire une variable catégorielle à partir des mannequins dans les pandas
pd.get_dummies permet de convertir une variable catégorique dans des variables nominales. Outre le fait qu'il est trivial de reconstruire la variable catégorique, est-il préféré/moyen rapide de le faire?
In[46]: s =Series(list('aaabbbccddefgh')).astype('category')In[47]: s
Out[47]:0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: category
Categories(8, object):[a < b < c < d < e < f < g < h]In[48]: df = pd.get_dummies(s)In[49]: df
Out[49]:
a b c d e f g h
0100000001100000002100000003010000004010000005010000006001000007001000008000100009000100001000001000110000010012000000101300000001In[50]: x = df.stack()# I don't think you actually need to specify ALL of the categories here, as by definition# they are in the dummy matrix to start (and hence the column index)In[51]:Series(pd.Categorical(x[x!=0].index.get_level_values(1)))Out[51]:0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
Name: level_1, dtype: category
Categories(8, object):[a < b < c < d < e < f < g < h]
Donc je pense que nous avons besoin d'une fonction de "faire" ce qu'il semble être d'un naturel opérations. Peut-être get_categories(), voir ici
Il y a quelques années, de sorte que cela peut ne pas avoir été dans le pandas trousse de retour lorsque cette question a été à l'origine posée, mais cette approche semble un peu plus facile pour moi. idxmax sera de retour l'indice correspondant à l'élément le plus large (c'est à dire celui avec un 1). Nous ne axis=1 parce que nous voulons que le nom de la colonne où la 1 se produit.
EDIT: je n'ai pas pris la peine qu'il est catégorique au lieu de simplement une chaîne de caractères, mais vous pouvez le faire de la même manière que @Jeff n'a en l'enveloppant avec pd.Categorical (et pd.Series, si désiré).
In[1]:import pandas as pd
In[2]: s = pd.Series(['a','b','a','c'])In[3]: s
Out[3]:0 a
1 b
2 a
3 c
dtype: object
In[4]: dummies = pd.get_dummies(s)In[5]: dummies
Out[5]:
a b c
0100101021003001In[6]: s2 = dummies.idxmax(axis=1)In[7]: s2
Out[7]:0 a
1 b
2 a
3 c
dtype: object
In[8]:(s2 == s).all()Out[8]:True
MODIFIER en réponse à @piRSquared commentaire:
Cette solution ne permet en effet de supposer il y a un 1 par ligne. Je pense que c'est généralement le format. pd.get_dummies pouvez retourner les lignes qui sont toutes à 0 si vous avez drop_first=True ou si il y a NaN valeurs et dummy_na=False (par défaut) (en tout cas je suis absent?). Une ligne de zéros, sera traitée comme si elle était une instance de la variable nommée dans la première colonne (par exemple, a dans l'exemple ci-dessus).
Si drop_first=True, vous n'avez aucun moyen de savoir à partir de mannequins dataframe seul ce que le nom de la "première" variable a été, de sorte que l'opération n'est pas inversible, sauf si vous gardez des informations supplémentaires autour; je voudrais vous recommandons de laisser drop_first=False (par défaut).
Depuis dummy_na=False est la valeur par défaut, cela pourrait causer des problèmes. Veuillez régler dummy_na=True lorsque vous appelez pd.get_dummies si vous souhaitez utiliser cette solution pour inverser le "dummification" et vos données contient tout NaNs. Réglage dummy_na=True sera toujours ajouter un "nan" colonne, même si cette colonne est 0, de sorte que vous ne voulez probablement pas à définir ce que si vous avez réellement NaNs. Une belle approche pourrait consister à mettre dummies = pd.get_dummies(series, dummy_na=series.isnull().any()). Ce qui est sympa, c'est que idxmax solution correctement régénérer votre NaNs (et pas seulement une chaîne de caractères qui dit "nan").
Il est également intéressant de mentionner que la mise drop_first=True et dummy_na=False signifie que NaNs deviennent impossibles à distinguer à partir d'une instance de la première variable, ce qui devrait être fortement déconseillée si votre jeu de données peut contenir n'importe quel NaN valeurs.
Cela échoue lorsqu'une ligne est tous les zéros. Il travaille pour cet exemple et sous l'hypothèse qu'un et un seul 1 valeur par ligne.
C'est tout à fait une réponse tardive, mais puisque vous demandez une rapide façon de le faire, je suppose que vous êtes à la recherche pour le plus performant de la stratégie. Sur un grand dataframe (par exemple 10000 lignes), vous pouvez obtenir une très importante augmentation de la vitesse en utilisant np.where au lieu de idxmax ou get_level_values, et obtenir obtenir le même résultat. L'idée est d'indexer les noms de colonne où le mannequin dataframe n'est pas 0:
Méthode:
En utilisant le même échantillon de données comme @Nathan:
>>> dummies
a b c
0100101021003001
s2 = pd.Series(dummies.columns[np.where(dummies!=0)[1]])>>> s2
0 a
1 b
2 a
3 c
dtype: object
Référence:
Sur un petit mannequin dataframe, vous ne verrez pas beaucoup de différence dans la performance. Cependant, le test de différentes stratégies pour la résolution de ce problème sur une grande série:
s = pd.Series(np.random.choice(['a','b','c'],10000))
dummies = pd.get_dummies(s)def np_method(dummies=dummies):return pd.Series(dummies.columns[np.where(dummies!=0)[1]])def idx_max_method(dummies=dummies):return dummies.idxmax(axis=1)def get_level_values_method(dummies=dummies):
x = dummies.stack()return pd.Series(pd.Categorical(x[x!=0].index.get_level_values(1)))def dot_method(dummies=dummies):return dummies.dot(dummies.columns)import timeit
# Time each method, 1000 iterations each:>>> timeit.timeit(np_method, number=1000)1.0491090340074152>>> timeit.timeit(idx_max_method, number=1000)12.119140846014488>>> timeit.timeit(get_level_values_method, number=1000)4.109266621991992>>> timeit.timeit(dot_method, number=1000)1.6741622970002936
La np.where méthode est environ 4 fois plus rapide que la get_level_values méthode de 11,5 fois plus rapide que la idxmax méthode! Il bat aussi (mais seulement un peu) la .dot() méthode décrite dans cette réponse à une question similaire
s =Series(list('aaabbbccddefgh')).astype('category')
df = pd.get_dummies(s)
Si les colonnes sont des chaînes de caractères
et il ya seulement un 1 par ligne
df.dot(df.columns)0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: object
numpy.where
De nouveau! En supposant que l'un 1 par ligne
i, j = np.where(df)
pd.Series(df.columns[j], i)0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: category
Categories(8, object):[a, b, c, d, e, f, g, h]
numpy.where
Pas en supposant que l'on 1 par ligne
i, j = np.where(df)
pd.Series(dict(zip(zip(i, j), df.columns[j])))00 a
10 a
20 a
31 b
41 b
51 b
62 c
72 c
83 d
93 d
104 e
115 f
126 g
137 h
dtype: object
numpy.where
Où nous n'imaginons pas un 1 par ligne et nous supprimer l'index
i, j = np.where(df)
pd.Series(dict(zip(zip(i, j), df.columns[j]))).reset_index(-1, drop=True)0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: object
Donc je pense que nous avons besoin d'une fonction de "faire" ce qu'il semble être d'un naturel opérations. Peut-être
get_categories()
, voir iciOriginalL'auteur
Il y a quelques années, de sorte que cela peut ne pas avoir été dans le
pandas
trousse de retour lorsque cette question a été à l'origine posée, mais cette approche semble un peu plus facile pour moi.idxmax
sera de retour l'indice correspondant à l'élément le plus large (c'est à dire celui avec un1
). Nous neaxis=1
parce que nous voulons que le nom de la colonne où la1
se produit.EDIT: je n'ai pas pris la peine qu'il est catégorique au lieu de simplement une chaîne de caractères, mais vous pouvez le faire de la même manière que @Jeff n'a en l'enveloppant avec
pd.Categorical
(etpd.Series
, si désiré).MODIFIER en réponse à @piRSquared commentaire:
Cette solution ne permet en effet de supposer il y a un
1
par ligne. Je pense que c'est généralement le format.pd.get_dummies
pouvez retourner les lignes qui sont toutes à 0 si vous avezdrop_first=True
ou si il y aNaN
valeurs etdummy_na=False
(par défaut) (en tout cas je suis absent?). Une ligne de zéros, sera traitée comme si elle était une instance de la variable nommée dans la première colonne (par exemple,a
dans l'exemple ci-dessus).Si
drop_first=True
, vous n'avez aucun moyen de savoir à partir de mannequins dataframe seul ce que le nom de la "première" variable a été, de sorte que l'opération n'est pas inversible, sauf si vous gardez des informations supplémentaires autour; je voudrais vous recommandons de laisserdrop_first=False
(par défaut).Depuis
dummy_na=False
est la valeur par défaut, cela pourrait causer des problèmes. Veuillez réglerdummy_na=True
lorsque vous appelezpd.get_dummies
si vous souhaitez utiliser cette solution pour inverser le "dummification" et vos données contient toutNaNs
. Réglagedummy_na=True
sera toujours ajouter un "nan" colonne, même si cette colonne est 0, de sorte que vous ne voulez probablement pas à définir ce que si vous avez réellementNaN
s. Une belle approche pourrait consister à mettredummies = pd.get_dummies(series, dummy_na=series.isnull().any())
. Ce qui est sympa, c'est queidxmax
solution correctement régénérer votreNaN
s (et pas seulement une chaîne de caractères qui dit "nan").Il est également intéressant de mentionner que la mise
drop_first=True
etdummy_na=False
signifie queNaN
s deviennent impossibles à distinguer à partir d'une instance de la première variable, ce qui devrait être fortement déconseillée si votre jeu de données peut contenir n'importe quelNaN
valeurs.1
valeur par ligne.OriginalL'auteur
C'est tout à fait une réponse tardive, mais puisque vous demandez une rapide façon de le faire, je suppose que vous êtes à la recherche pour le plus performant de la stratégie. Sur un grand dataframe (par exemple 10000 lignes), vous pouvez obtenir une très importante augmentation de la vitesse en utilisant
np.where
au lieu deidxmax
ouget_level_values
, et obtenir obtenir le même résultat. L'idée est d'indexer les noms de colonne où le mannequin dataframe n'est pas 0:Méthode:
En utilisant le même échantillon de données comme @Nathan:
Référence:
Sur un petit mannequin dataframe, vous ne verrez pas beaucoup de différence dans la performance. Cependant, le test de différentes stratégies pour la résolution de ce problème sur une grande série:
La
np.where
méthode est environ 4 fois plus rapide que laget_level_values
méthode de 11,5 fois plus rapide que laidxmax
méthode! Il bat aussi (mais seulement un peu) la.dot()
méthode décrite dans cette réponse à une question similaireIls retournent tous au même résultat:
OriginalL'auteur
Installation
À l'aide de @Jeff installation
Si les colonnes sont des chaînes de caractères
et il ya seulement un
1
par lignenumpy.where
De nouveau! En supposant que l'un
1
par lignenumpy.where
Pas en supposant que l'on
1
par lignenumpy.where
Où nous n'imaginons pas un
1
par ligne et nous supprimer l'indexOriginalL'auteur