web-dev-qa-db-fra.com

Remapper les valeurs dans la colonne pandas avec un dict

J'ai un dictionnaire qui ressemble à ceci: di = {1: "A", 2: "B"}

Je voudrais l'appliquer à la colonne "col1" d'un cadre de données similaire à:

     col1   col2
0       w      a
1       1      2
2       2    NaN

obtenir:

     col1   col2
0       w      a
1       A      2
2       B    NaN

Comment puis-je le faire au mieux? Pour une raison quelconque, les termes de recherche sur Google relatifs à cela ne me montrent que des liens sur la création de colonnes à partir de dict et vice-versa: - / 

180
TheChymera

Vous pouvez utiliser .replace . Par exemple:

>>> df = pd.DataFrame({'col2': {0: 'a', 1: 2, 2: np.nan}, 'col1': {0: 'w', 1: 1, 2: 2}})
>>> di = {1: "A", 2: "B"}
>>> df
  col1 col2
0    w    a
1    1    2
2    2  NaN
>>> df.replace({"col1": di})
  col1 col2
0    w    a
1    A    2
2    B  NaN

ou directement sur la Series , c.-à-d. df["col1"].replace(di, inplace=True).

211
DSM

map peut être beaucoup plus rapide que replace

Si votre dictionnaire comporte plus de deux clés, utiliser map peut être beaucoup plus rapide que replace. Il existe deux versions de cette approche, selon que votre dictionnaire mappe de manière exhaustive toutes les valeurs possibles (et que vous souhaitiez également que les non-correspondances conservent leurs valeurs ou soient converties en NaN):

Cartographie exhaustive

Dans ce cas, le formulaire est très simple:

df['col1'].map(di)       # note: if the dictionary does not exhaustively map all
                         # entries then non-matched entries are changed to NaNs

Bien que map prenne le plus souvent une fonction comme argument, il peut aussi prendre un dictionnaire ou une série: Documentation for Pandas.series.map

Cartographie non exhaustive

Si vous avez un mappage non exhaustif et souhaitez conserver les variables existantes pour les non-correspondances, vous pouvez ajouter fillna:

df['col1'].map(di).fillna(df['col1'])

comme dans la réponse de @ jpp ici: Remplacer efficacement les valeurs d'une série de pandas via le dictionnaire

Des repères

Utilisation des données suivantes avec Pandas version 0.23.1:

di = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E", 6: "F", 7: "G", 8: "H" }
df = pd.DataFrame({ 'col1': np.random.choice( range(1,9), 100000 ) })

et en testant avec %timeit, il apparaît que map est environ 10 fois plus rapide que replace

Notez que votre accélération avec map variera avec vos données. La plus grande accélération semble concerner les grands dictionnaires et les remplacements exhaustifs. Voir la réponse @jpp (lien ci-dessus) pour des repères plus détaillés et une discussion.

95
JohnE

Il y a un peu d'ambiguïté dans votre question. Il y a au moins trois deux interprétations:

  1. les clés dans di font référence aux valeurs d'index
  2. les clés dans di font référence à df['col1'] valeurs
  3. les touches dans di font référence aux emplacements d'index (pas la question du PO, mais insérées pour le plaisir.)

Vous trouverez ci-dessous une solution pour chaque cas.


Cas 1: Si les clés de di sont censées faire référence à des valeurs d'index, vous pouvez utiliser la méthode update:

df['col1'].update(pd.Series(di))

Par exemple,

import pandas as pd
import numpy as np

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
#   col1 col2
# 1    w    a
# 2   10   30
# 0   20  NaN

di = {0: "A", 2: "B"}

# The value at the 0-index is mapped to 'A', the value at the 2-index is mapped to 'B'
df['col1'].update(pd.Series(di))
print(df)

les rendements

  col1 col2
1    w    a
2    B   30
0    A  NaN

J'ai modifié les valeurs de votre message d'origine afin de clarifier ce que update fait . Notez comment les clés de di sont associées à des valeurs d'index. L'ordre des valeurs d'index, c'est-à-dire l'indice locations - n'a pas d'importance.


Cas 2: Si les clés dans di font référence à df['col1'] valeurs, alors @DanAllan et @DSM montrent comment y parvenir avec replace:

import pandas as pd
import numpy as np

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
print(df)
#   col1 col2
# 1    w    a
# 2   10   30
# 0   20  NaN

di = {10: "A", 20: "B"}

# The values 10 and 20 are replaced by 'A' and 'B'
df['col1'].replace(di, inplace=True)
print(df)

les rendements

  col1 col2
1    w    a
2    A   30
0    B  NaN

Notez comment, dans ce cas, les clés dans di ont été modifiées pour correspondre à values ​​ dans df['col1'].


Cas 3: Si les clés dans di font référence à des emplacements d'index, vous pourriez alors utiliser

df['col1'].put(di.keys(), di.values())

puisque

df = pd.DataFrame({'col1':['w', 10, 20],
                   'col2': ['a', 30, np.nan]},
                  index=[1,2,0])
di = {0: "A", 2: "B"}

# The values at the 0 and 2 index locations are replaced by 'A' and 'B'
df['col1'].put(di.keys(), di.values())
print(df)

les rendements

  col1 col2
1    A    a
2   10   30
0    B  NaN

Ici, les première et troisième lignes ont été modifiées, car les clés dans di sont 0 et 2, qui, avec l'indexation à base de Python à 0, font référence aux premier et troisième emplacements.

51
unutbu

Ajout à cette question si vous avez déjà plusieurs colonnes à remapper dans un cadre de données de données:

def remap(data,dict_labels):
    """
    This function take in a dictionnary of labels : dict_labels 
    and replace the values (previously labelencode) into the string.

    ex: dict_labels = {{'col1':{1:'A',2:'B'}}

    """
    for field,values in dict_labels.items():
        print("I am remapping %s"%field)
        data.replace({field:values},inplace=True)
    print("DONE")

    return data

J'espère que cela peut être utile à quelqu'un.

À votre santé

3
Nico Coallier

DSM a la réponse acceptée, mais le codage ne semble pas fonctionner pour tout le monde. En voici une qui fonctionne avec la version actuelle de pandas (0.23.4 à partir du 8/2018):

import pandas as pd

df = pd.DataFrame({'col1': [1, 2, 2, 3, 1],
            'col2': ['negative', 'positive', 'neutral', 'neutral', 'positive']})

conversion_dict = {'negative': -1, 'neutral': 0, 'positive': 1}
df['converted_column'] = df['col2'].replace(conversion_dict)

print(df.head())

Vous verrez que ça ressemble à:

   col1      col2  converted_column
0     1  negative                -1
1     2  positive                 1
2     2   neutral                 0
3     3   neutral                 0
4     1  positive                 1

Les docs pour pandas.DataFrame.replace sont ici .

1
wordsforthewise

Ou est-ce que apply:

df['col1'].apply(lambda x: {1: "A", 2: "B"}.get(x,x))

Démo:

>>> df['col1']=df['col1'].apply(lambda x: {1: "A", 2: "B"}.get(x,x))
>>> df
  col1 col2
0    w    a
1    1    2
2    2  NaN
>>> 
0
U9-Forward

Une approche plus native des pandas consiste à appliquer une fonction de remplacement comme ci-dessous:

def multiple_replace(dict, text):
  # Create a regular expression  from the dictionary keys
  regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))

  # For each match, look-up corresponding value in dictionary
  return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) 

Une fois que vous avez défini la fonction, vous pouvez l’appliquer à votre cadre de données.

di = {1: "A", 2: "B"}
df['col1'] = df.apply(lambda row: multiple_replace(di, row['col1']), axis=1)
0
Amirhos Imani

Une solution complète et agréable qui garde une carte de vos étiquettes de classe:

labels = features['col1'].unique()
labels_dict = dict(Zip(labels, range(len(labels))))
features = features.replace({"col1": labels_dict})

De cette façon, vous pouvez à tout moment faire référence à l'étiquette de classe d'origine de labels_dict.

0
dorien