pandas.Seriesのmapメソッドで列の要素を置換
pandas.Series
のmap()
メソッドに辞書dict
を指定すると要素を置換できる。replace()
メソッドでも同様に要素の置換ができるが、条件によってはmap()
のほうが高速になる場合がある。
map()
は任意の関数を各要素に適用する処理にも使われる。
本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。
import pandas as pd
print(pd.__version__)
# 2.1.2
要素の置換におけるmap()とreplace()の違い
map()
に辞書dict
({key: value
)を指定すると、キーkey
と一致する要素が値value
に置き換えられる。
s = pd.Series(['A', 'B', 'C', 'A', 'B'])
print(s)
# 0 A
# 1 B
# 2 C
# 3 A
# 4 B
# dtype: object
print(s.map({'A': 'XX', 'B': 'YY', 'C': 'ZZ'}))
# 0 XX
# 1 YY
# 2 ZZ
# 3 XX
# 4 YY
# dtype: object
replace()
にも辞書を指定できる。Series
のすべての要素に対して置換が行われる場合はmap()
と同様の結果となる。
print(s.replace({'A': 'XX', 'B': 'YY', 'C': 'ZZ'}))
# 0 XX
# 1 YY
# 2 ZZ
# 3 XX
# 4 YY
# dtype: object
辞書のキーがSeries
のすべての要素の値を網羅していない場合は結果が異なる。map()
では置換されない値がNaN
となるが、replace()
では元の値のまま。
print(s.map({'A': 'XX'}))
# 0 XX
# 1 NaN
# 2 NaN
# 3 XX
# 4 NaN
# dtype: object
print(s.replace({'A': 'XX'}))
# 0 XX
# 1 B
# 2 C
# 3 XX
# 4 B
# dtype: object
map()
で置換されない要素を元の値のままにするには、NaN
を埋めるfillna()
メソッドに元のSeries
を指定する。
print(s.map({'A': 'XX'}).fillna(s))
# 0 XX
# 1 B
# 2 C
# 3 XX
# 4 B
# dtype: object
なお、replace()
では正規表現を使って文字列の一部のみを置換したり、DataFrame
の列ごとに異なる値に置換したりするなど、より複雑な処理が可能。詳細は以下の記事を参照。
速度比較
Jupyter Notebookのマジックコマンド%%timeit
でmap()
とreplace()
の実行時間を計測した結果を示す。Pythonコードとして実行しても計測できないので注意。
要素数100個のSeries
を例とする。
s = pd.Series(range(100))
すべての要素に対して置換が行われる場合はmap()
のほうがreplace()
よりも高速。
d_100 = {i: i * 10 for i in range(100)}
%%timeit
s.map(d_100)
# 70.7 µs ± 2.08 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%%timeit
s.replace(d_100)
# 1.31 ms ± 26.7 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
要素数50個の辞書で置換する場合もmap()
とfillna()
のほうがreplace()
よりも高速。
d_50 = {i: i * 10 for i in range(50)}
%%timeit
s.map(d_50).fillna(s)
# 108 µs ± 3.1 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%%timeit
s.replace(d_50)
# 653 µs ± 3.73 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
要素数5個の辞書で置換する場合はreplace()
のほうがmap()
とfillna()
よりも高速。
d_5 = {i: i * 10 for i in range(5)}
%%timeit
s.map(d_5).fillna(s)
# 104 µs ± 3.85 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%%timeit
s.replace(d_5)
# 78.5 µs ± 860 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
このように、replace()
の実行時間は辞書のサイズに大きく依存する。
実行環境などによっても結果が変わる可能性もあるので、処理速度が重要な処理であれば実際に置換を行う条件でmap()
とreplace()
を試してみてからどちらを使うか決定するとよいだろう。