2017-10-16 4 views
2

Comment utiliser les pandas factorize avec des valeurs qui existent sur deux colonnes? Plus précisément, j'essaie de convertir des valeurs qui existent dans deux colonnes en valeurs numériques, et de mettre les valeurs factorisées correspondantes dans de nouvelles colonnes, de sorte que la factorisation soit cohérente avec les deux colonnes d'entrée «A» et «B».Factoriser des valeurs sur des colonnes de données avec des mappages cohérents

existant dataframe:

 A B 
0 a b 
1 c a 
2 d a 
3 e c 
4 c b 
5 b e 
6 e f 

souhaité Sortie:

 A B A_ID B_ID 
0 a b  0  4 
1 c a  1  0 
2 d a  2  0 
3 e c  3  1 
4 c b  1  4 
5 b e  4  3 
6 e f  3  5 

Je suis en mesure d'utiliser factorize avec succès pour une colonne en utilisant:

df['A_ID'] = pd.factorize(df.A)[0] 

Comment pourrais-je accomplir cela avec cohérence mappages pour les valeurs sur deux colonnes? Ai-je besoin d'utiliser une fonction personnalisée lambda à la place, ou y a-t-il un moyen d'accomplir ceci avec factorize?

Répondre

1

pd.factorize, apply + pd.Categorical:

_, b = pd.factorize(df.values.T.reshape(-1,)) 
          # or df.values.ravel('F'), as suggested by Zero 
r = df.apply(lambda x: pd.Categorical(x, b).codes).add_suffix('_ID') 

    A_ID B_ID 
0  0  4 
1  1  0 
2  2  0 
3  3  1 
4  1  4 
5  4  3 
6  3  5 

pd.concat([df, r], 1) 

    A B A_ID B_ID 
0 a b  0  4 
1 c a  1  0 
2 d a  2  0 
3 e c  3  1 
4 c b  1  4 
5 b e  4  3 
6 e f  3  5 
+1

J'aime l'utilisation du remodelage numpy dans cette solution.+1 –

+0

@ScottBoston Retourné avec intérêt :-) –

+1

Vous pourriez faire 'df.values.ravel ('F')' inplace de 'df.values.T.reshape (-1,)' aussi – Zero

1

Voici une façon, si vous voulez réutiliser les valeurs factorielles.

In [2637]: facts = np.unique(np.unique(df[['A', 'B']]), return_index=True) 

In [2638]: mapping = dict(zip(*facts)) 

In [2639]: df.join(df[['A', 'B']].apply(lambda x: x.map(mapping)).add_suffix('_ID')) 
Out[2639]: 
    A B A_ID B_ID 
0 a b  0  1 
1 c a  2  0 
2 d a  3  0 
3 e c  4  2 
4 c b  2  1 
5 b e  1  4 
6 e f  4  5 

Ou, en utilisantreplace

In [2640]: df.join(df[['A', 'B']].replace(mapping).add_suffix('_ID')) 
Out[2640]: 
    A B A_ID B_ID 
0 a b  0  1 
1 c a  2  0 
2 d a  3  0 
3 e c  4  2 
4 c b  2  1 
5 b e  1  4 
6 e f  4  5 

Et, pour préserver votre commande de valeurs utiliser

In [2]: mapping = dict(zip(*pd.factorize(df['A'].append(df['B']).drop_duplicates())[::-1])) 

In [2]: mapping 
Out[2666]: {'a': 0, 'b': 4, 'c': 1, 'd': 2, 'e': 3, 'f': 5} 

In [3]: df.join(df[['A', 'B']].replace(mapping).add_suffix('_ID')) 
Out[3]: 
    A B A_ID B_ID 
0 a b  0  4 
1 c a  1  0 
2 d a  2  0 
3 e c  3  1 
4 c b  1  4 
5 b e  4  3 
6 e f  3  5 

Détails

In [2641]: facts 
Out[2641]: 
(array(['a', 'b', 'c', 'd', 'e', 'f'], dtype=object), 
array([0, 1, 2, 3, 4, 5], dtype=int64)) 

In [2642]: mapping 
Out[2642]: {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5} 
+0

Merci @Zero, cela fonctionne pour ma question initiale où toutes les valeurs de la colonne B sont également présents dans la colonne A. Je mis à jour la question où la colonne B a une valeur qui n'est pas dans la colonne A. Comment pourrais-je résoudre pour ce cas? Actuellement, 'B_ID' renverra' NaN' dans ce cas, car la valeur n'existe pas dans 'facts'. De plus, sur mon système, les valeurs 'B_ID' reviennent en double (4.0, 0.0, ...). Comment puis-je les convertir en int pendant ce processus, pour correspondre à 'A_ID'? –

2

Utilisons apply, add_suffix avec pd.factorize et assign:

f = pd.factorize(df.stack().drop_duplicates().sort_index(level=1)) 
s1 = pd.Series(f[0], index=f[1]) 
print(df.assign(**df.apply(lambda x: x.map(s1)).add_suffix('_ID'))) 

Sortie:

A B A_ID B_ID 
0 a b  0  1 
1 c a  2  0 
2 d a  3  0 
3 e c  4  2 
4 c b  2  1 
5 b e  1  4 

Ouput avec jeu de données mise à jour:

A B A_ID B_ID 
0 a b  0  4 
1 c a  1  0 
2 d a  2  0 
3 e c  3  1 
4 c b  1  4 
5 b e  4  3 
6 e f  3  5