辞書を使用してパンダ列の値を再マッピングする


318

次のような辞書があります。 di = {1: "A", 2: "B"}

次のようなデータフレームの「col1」列にそれを適用したいと思います。

     col1   col2
0       w      a
1       1      2
2       2    NaN

取得するため:

     col1   col2
0       w      a
1       A      2
2       B    NaN

これを行うにはどうすればよいですか?何らかの理由で、これに関連する用語をググリングすると、dictsから列を作成する方法とその逆の方法に関するリンクのみが表示されます:-/

回答:


341

使用できます.replace。例えば:

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

または直接Series、すなわちdf["col1"].replace(di, inplace=True)


1
col```` is tuple. The error info is 型 'ndarray(dtype = object)'と 'tuple'```を比較できない場合、それは私には機能しません
Zhao

18
これはもう動作しないように見え、すべての答えは4年前からだっ与え驚くべきことではないされています、。操作がいかに一般的であるかを考えると、この質問には新しい回答が必要です...
PrestonH 2017年

2
@PrestonHそれは私には完璧に動作します。実行中:'3.6.1 |Anaconda custom (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]'
Dan

わたしにはできる。しかし、すべての列の値を置き換える場合はどうすればよいですか?
famargar

2
表示された回答の中で私のために機能した唯一の方法は、シリーズを直接置き換えることでした。ありがとう!
Dirigo 2018年

243

map よりもはるかに高速にすることができます replace

辞書に2つ以上のキーがある場合mapは、を使用する方がはるかに高速ですreplace。このアプローチには2つのバージョンがあり、ディクショナリがすべての可能な値を完全にマッピングするかどうか(また、不一致で値を保持するか、NaNに変換するか)によって異なります。

徹底的なマッピング

この場合、フォームは非常に単純です。

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

けれどもmap:最も一般的にその引数として関数を受け取り、それが代わりに辞書やシリーズ取ることができます Pandas.series.mapのドキュメントの

非網羅的なマッピング

完全ではないマッピングがあり、一致しない場合に既存の変数を保持したい場合は、以下を追加できますfillna

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

@jppの回答のように: 辞書を介してパンダシリーズの値を効率的に置き換えます

ベンチマーク

パンダバージョン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 ) })

を使用してテストすると%timeit、のmap約10倍高速であるように見えますreplace

のスピードアップはmapデータによって異なります。最大の高速化は、大規模な辞書と徹底的な置き換えによるものです。より広範なベンチマークとディスカッションについては、@ jppの回答(上記にリンク)を参照してください。


17
この回答のコードの最後のブロックは確かに最もエレガントではありませんが、この回答はある程度の価値があります。大規模な辞書の場合は桁違いに速く、RAMをすべて使用しません。30分で約900万エントリの辞書を使用して、10,000行のファイルを再マップしました。このdf.replace機能は整頓されていて、小さな辞書に役立ちますが、20分間ほど実行するとクラッシュしました。
griffinc


@griffincフィードバックに感謝します。それ以降、この回答を更新して、網羅的ではないケースを実行する非常に簡単な方法を使用しました(@jppに感謝)
JohnE

1
map私はそれを行う方法を理解できなかったインデックスでも機能しますreplace
Max Ghenis

1
@AlexSB完全に一般的な答えを出すことはできませんが、マップの方がはるかに速く、同じことを実現できると思います。一般に、マージは同じことを行う他のオプションよりも遅くなります。
JohnE

59

あなたの質問には少し曖昧な点があります。少なくともあります3つの二つの解釈は:

  1. のキーはdiインデックス値を参照します
  2. のキーは値をdi参照しdf['col1']ます
  3. のキーはdiインデックスの場所を参照しています(OPの質問ではありませんが、楽しみのためにスローされます)。

以下は、それぞれの場合の解決策です。


ケース1: のキーがdiインデックス値を参照することを意図している場合、updateメソッドを使用できます。

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

例えば、

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)

収量

  col1 col2
1    w    a
2    B   30
0    A  NaN

元の投稿の値を変更して、何updateが行われているのかを明確にしました。のキーがどのようにdiインデックス値に関連付けられているかに注意してください。インデックス値の順序、つまりインデックスの場所は関係ありません。


ケース2: のキーが値をdi参照しているdf['col1']場合、@ DanAllanと@DSMはこれを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)

収量

  col1 col2
1    w    a
2    A   30
0    B  NaN

この場合のキーdiがのと一致するように変更されたことに注意してくださいdf['col1']


ケース3: キーがdiインデックスの場所を参照している場合は、次を使用できます

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

以来

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)

収量

  col1 col2
1    A    a
2   10   30
0    B  NaN

ここでは、最初の行と3番目の行が変更されています。これは、Pythonの0ベースのインデックスでは、最初と3番目の場所を参照するand のキーが使用されてdiいるためです。02


replace同様に良いです、そしておそらくここで何が起こっているかについてより良い言葉です。
Dan Allan

OPのポストされたターゲットデータフレームは、あいまいさを排除していませんか?それでも、この回答は役に立ちます。+ 1してください。
DSM

@DSM:おっと、そうです、Case3の可能性はありませんが、インデックス値が列の値と等しいため、OPのターゲットデータフレームがCase1とCase2を区別するとは思いません。
unutbu 2013年

投稿された他の多くの人たちと同様に、残念ながら@DSMの方法はうまくいきませんでしたが、@ unutbuのケース1はうまくいきました。update()に比べると少し不器用に見えますreplace()が、少なくともそれは機能します。
Geoff、

4

データデータフレームで再マップする列が複数ある場合は、この質問に追加します。

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

それが誰かに役立つことを願っています。

乾杯


1
この機能はすでにによって提供されていますがDataFrame.replace()、いつ追加されたかはわかりません。
AMC

3

DSMは受け入れられた答えを持っていますが、コーディングはすべての人にとってうまくいくようには見えません。これがパンダの現在のバージョン(2018年8月の時点で0.23.4)で動作するものです:

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

次のように表示されます。

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

pandas.DataFrame.replaceのドキュメントはこちらです。


DSMの回答を取得するのに問題があったことは一度もありませんでした。投票率が高かったことを考えると、他のほとんどの人もそうではなかったと思います。発生している問題をより具体的にしたい場合があります。多分それはDSMのものとは異なるあなたのサンプルデータと関係があるのでしょうか?
JohnE 2018

おそらく、バージョン管理の問題です。それにもかかわらず、両方の答えがここにあります。
ワードフォーザワイズ2018

1
受け入れられた回答の解決策は特定のタイプでのみ機能し、Series.map()より柔軟に見えます。
AMC

2

またはapply

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

デモ:

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

お使いの際に何が起きるdiのdictは、リストの辞書のですか?リスト内の値を1つだけマッピングするにはどうすればよいですか?
FaCoffee

できますが、なぜそうなるのかはわかりません。
AMC

2

与えられたmap方が置換よりも速い(@JohnEの解決策)特定の値をにマッピングする予定の非NaN網羅的なマッピングには注意する必要があります。この場合の適切な方法ではmask、シリーズを作成する必要があります。.fillnaそれ以外の場合は、マッピングを元に戻しますNaN

import pandas as pd
import numpy as np

d = {'m': 'Male', 'f': 'Female', 'missing': np.NaN}
df = pd.DataFrame({'gender': ['m', 'f', 'missing', 'Male', 'U']})

keep_nan = [k for k,v in d.items() if pd.isnull(v)]
s = df['gender']

df['mapped'] = s.map(d).fillna(s.mask(s.isin(keep_nan)))

    gender  mapped
0        m    Male
1        f  Female
2  missing     NaN
3     Male    Male
4        U       U

1

クラスラベルのマップを保持する素晴らしい完全なソリューション:

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

このようにして、labels_dictの元のクラスラベルをいつでも参照できます。


1

Nico Coallier(複数の列に適用)およびU10-Forward(メソッドの適用スタイルを使用)によって提案されたものへの拡張として、それを要約して1行にまとめます。

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

は、.transform()各列をシリーズとして処理します。.apply()DataFrameに集約された列を渡すのとは逆です。

したがって、Seriesメソッドを適用できますmap()

最後に、U10のおかげでこの動作を発見しました。.get()式でシリーズ全体を使用できます。私がその動作を誤解していない限り、それはビットごとではなく連続的にシリーズを処理します。マッピングディクショナリで言及しなかった値
.get(x,x)アカウント。これは、.map()メソッドによってナンと見なされます。


は、.transform()各列をシリーズとして処理します。.apply()DataFrameに集約された列を渡すのとは逆です。私は試してみましたが、apply()うまくいきます。locどちらも使用する必要はありません。これは非常に複雑に見えます。df[["col1", "col2"]].apply(lambda col: col.map(lambda elem: my_dict.get(elem, elem)))正常に動作するはずです。マッピングディクショナリで言及しなかった値.get(x,x)アカウント。これは、.map()メソッドによってそれ以外の場合はNanと見なされfillna()ます。後で使用することもできます。
AMC

最後に、U10のおかげでこの動作を発見しました。.get()式でシリーズ全体を使用できます。私がその動作を誤解していない限り、それはビットごとではなく連続的にシリーズを処理します。これは再現できません。詳しく説明していただけますか?同じ名前の変数がここで何らかの役割を果たす可能性があります。
AMC

0

よりネイティブなパンダのアプローチは、以下のように置換関数を適用することです:

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) 

関数を定義したら、それをデータフレームに適用できます。

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

よりネイティブなパンダのアプローチは、以下のように置換関数を適用することです。パンダによって提供されるはるかに単純なメソッドよりも「ネイティブ」(慣用的)がいかに多いか。
AMC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.