Pandasシリーズs
の値を辞書を介して置き換える方法d
は、何度も尋ねられ、再尋ねられました。
推奨される方法( 1 、 2 、、 4 )は、s.replace(d)
を使用するか、または、すべての系列値が辞書のキーで見つかった場合は、s.map(d)
を使用することがあります。
ただし、s.replace
を使用したパフォーマンスは、多くの場合、不合理に遅く、単純なリスト内包よりも5〜10倍遅くなります。
代替のs.map(d)
はパフォーマンスが優れていますが、ディクショナリですべてのキーが見つかった場合にのみお勧めします。
なぜs.replace
は非常に遅いのですか、またパフォーマンスをどのように改善できますか?
import pandas as pd, numpy as np
df = pd.DataFrame({'A': np.random.randint(0, 1000, 1000000)})
lst = df['A'].values.tolist()
##### TEST 1 #####
d = {i: i+1 for i in range(1000)}
%timeit df['A'].replace(d) # 1.98s
%timeit [d[i] for i in lst] # 134ms
##### TEST 2 #####
d = {i: i+1 for i in range(10)}
%timeit df['A'].replace(d) # 20.1ms
%timeit [d.get(i, i) for i in lst] # 243ms
注:この質問は、いつ使用するかについての具体的なアドバイスを探しているため、重複としてマークされていません異なるデータセットを与えられた異なるメソッド。これは答えで明示的であり、他の質問では通常扱われない側面です。
簡単な解決策の1つは、値がディクショナリキーによってどの程度完全にカバーされているかの推定値に依存する方法を選択することです。
一般的なケース
df['A'].map(d)
を使用します。またはdf['A'].map(d).fillna(df['A']).astype(int)
を使用します。少数、たとえば5%未満、dの値
df['A'].replace(d)
を使用します約5%の「クロスオーバーポイント」は、以下のベンチマークに固有のものです。
興味深いことに、単純なリスト内包表記は通常、どちらのシナリオでもmap
を下回ります。
ベンチマーク
_import pandas as pd, numpy as np
df = pd.DataFrame({'A': np.random.randint(0, 1000, 1000000)})
lst = df['A'].values.tolist()
##### TEST 1 - Full Map #####
d = {i: i+1 for i in range(1000)}
%timeit df['A'].replace(d) # 1.98s
%timeit df['A'].map(d) # 84.3ms
%timeit [d[i] for i in lst] # 134ms
##### TEST 2 - Partial Map #####
d = {i: i+1 for i in range(10)}
%timeit df['A'].replace(d) # 20.1ms
%timeit df['A'].map(d).fillna(df['A']).astype(int) # 111ms
%timeit [d.get(i, i) for i in lst] # 243ms
_
説明
_s.replace
_が非常に遅い理由は、単に辞書をマップするだけではありません。これは、いくつかのEdgeケースと間違いなくまれな状況を扱います。通常、どのケースでもより注意が必要です。
これは、 _pandas\generic.py
_ のreplace()
からの抜粋です。
_items = list(compat.iteritems(to_replace))
keys, values = Zip(*items)
are_mappings = [is_dict_like(v) for v in values]
if any(are_mappings):
# handling of nested dictionaries
else:
to_replace, value = keys, values
return self.replace(to_replace, value, inplace=inplace,
limit=limit, regex=regex)
_
多くの手順が含まれているようです。
これは、 _pandas\series.py
_ のmap()
のより無駄のないコードと比較できます。
_if isinstance(arg, (dict, Series)):
if isinstance(arg, dict):
arg = self._constructor(arg, index=arg.keys())
indexer = arg.index.get_indexer(values)
new_values = algos.take_1d(arg._values, indexer)
_