web-dev-qa-db-fra.com

pandas.factorize sur un bloc de données entier

pandas.factorize code les valeurs d'entrée sous forme de type énuméré ou de variable catégorique. 

Mais comment puis-je convertir facilement et efficacement plusieurs colonnes d'un bloc de données? Qu'en est-il de l'étape de mappage inverse?

Exemple: Ce cadre de données contient des colonnes avec des valeurs de chaîne telles que "type 2" que je voudrais convertir en valeurs numériques - et éventuellement les traduire ultérieurement.

 enter image description here

12
clstaudt

Vous pouvez utiliser apply si vous devez factorize chaque colonne séparément:

df = pd.DataFrame({'A':['type1','type2','type2'],
                   'B':['type1','type2','type3'],
                   'C':['type1','type3','type3']})

print (df)
       A      B      C
0  type1  type1  type1
1  type2  type2  type3
2  type2  type3  type3

print (df.apply(lambda x: pd.factorize(x)[0]))
   A  B  C
0  0  0  0
1  1  1  1
2  1  2  1

Si vous avez besoin de la même valeur numérique pour la même valeur de chaîne:

print (df.stack().rank(method='dense').unstack())
     A    B    C
0  1.0  1.0  1.0
1  2.0  2.0  3.0
2  2.0  3.0  3.0

Si vous devez appliquer la fonction uniquement à certaines colonnes, utilisez un sous-ensemble:

df[['B','C']] = df[['B','C']].stack().rank(method='dense').unstack()
print (df)
       A    B    C
0  type1  1.0  1.0
1  type2  2.0  3.0
2  type2  3.0  3.0

Solution avec factorize :

stacked = df[['B','C']].stack()
df[['B','C']] = pd.Series(stacked.factorize()[0], index=stacked.index).unstack()
print (df)
       A  B  C
0  type1  0  0
1  type2  1  2
2  type2  2  2

Les convertir en arrière est possible via map by dict, où vous devez supprimer les doublons par drop_duplicates :

vals = df.stack().drop_duplicates().values
b = [x for x in df.stack().drop_duplicates().rank(method='dense')]

d1 = dict(Zip(b, vals))
print (d1)
{1.0: 'type1', 2.0: 'type2', 3.0: 'type3'}

df1 = df.stack().rank(method='dense').unstack()
print (df1)
     A    B    C
0  1.0  1.0  1.0
1  2.0  2.0  3.0
2  2.0  3.0  3.0

print (df1.stack().map(d1).unstack())
       A      B      C
0  type1  type1  type1
1  type2  type2  type3
2  type2  type3  type3
24
jezrael

J'ai également trouvé cette réponse très utile: https://stackoverflow.com/a/20051631/4643212

J'essayais de prendre les valeurs d'une colonne existante d'un Pandas DataFrame (une liste d'adresses IP nommées "SrcIP") et de les mapper à des valeurs numériques dans une nouvelle colonne (nommée "ID" dans cet exemple).

Solution:

df['ID'] = pd.factorize(df.SrcIP)[0]

Résultat:

        SrcIP | ID    
192.168.1.112 |  0  
192.168.1.112 |  0  
192.168.4.118 |  1 
192.168.1.112 |  0
192.168.4.118 |  1
192.168.5.122 |  2
192.168.5.122 |  2
...
3
Gabe F.

Je voudrais rediriger ma réponse: https://stackoverflow.com/a/32011969/1694714

Ancienne réponse 

Une autre solution lisible à ce problème, lorsque vous souhaitez conserver les catégories cohérentes dans le DataFrame résultant, est de remplacer:

def categorise(df):
    categories = {k: v for v, k in enumerate(df.stack().unique())}
    return df.replace(categories)

Effectue légèrement moins bien que l'exemple de @jezrael, mais plus facile à lire. En outre, il pourrait mieux s’aggraver pour de plus grands ensembles de données. Je peux faire des tests appropriés si quelqu'un est intéressé.

0
tbrittoborges