note.nkmk.me

pandas.Seriesのmapメソッドで列の要素を置換

Date: 2018-04-07 / tags: Python, pandas

pandas.Seriesmap()は、引数に関数を渡すことでpandas.Seriesの各要素に関数を適用するメソッド。

map()の引数には辞書型dictを指定することもできて、その場合は要素の置換になる。

要素の置換を行うメソッドにはreplace()があるが、pandas.Seriesまたはpandas.DataFrameの列(=pandas.Series)のすべての要素を別の値に置換するのであれば、map()のほうが高速になる場合が多い。

ここでは、

  • 要素の置換におけるmap()replace()の違い
    • 速度比較
  • map()の活用例: 質的データを量的データに変換

について説明する。

例として以下のデータを使用する。

import pandas as pd

df = pd.read_csv('data/src/sample_pandas_normal.csv')

print(df)
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 3     Dave   68    TX     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57

s = df['state']
print(s)
# 0    NY
# 1    CA
# 2    CA
# 3    TX
# 4    CA
# 5    NY
# Name: state, dtype: object

サンプルのcsvファイルはコチラ。

スポンサーリンク

要素の置換におけるmap()とreplace()の違い

map()の引数に辞書dict{key: value})を指定すると、keyと一致する要素がvalueに置き換えられる。

s_map_all = s.map({'NY': 'NewYork', 'CA': 'California', 'TX': 'Texas'})
print(s_map_all)
# 0       NewYork
# 1    California
# 2    California
# 3         Texas
# 4    California
# 5       NewYork
# Name: state, dtype: object

replace()の引数にも辞書を指定することが可能。pandas.Seriesのすべての要素に対して置換が行われる場合はreplace()と同様の結果となる。

s_replace_all = s.replace({'NY': 'NewYork', 'CA': 'California', 'TX': 'Texas'})
print(s_replace_all)
# 0       NewYork
# 1    California
# 2    California
# 3         Texas
# 4    California
# 5       NewYork
# Name: state, dtype: object

結果が異なるのは、辞書のkeypandas.Seriesのすべての要素の値を網羅していない場合。

map()では置換されない値がNaNとなってしまう。replace()では元の値のまま。

s_map = s.map({'NY': 'NewYork'})
print(s_map)
# 0    NewYork
# 1        NaN
# 2        NaN
# 3        NaN
# 4        NaN
# 5    NewYork
# Name: state, dtype: object

s_replace = s.replace({'NY': 'NewYork'})
print(s_replace)
# 0    NewYork
# 1         CA
# 2         CA
# 3         TX
# 4         CA
# 5    NewYork
# Name: state, dtype: object

map()update()を組み合わせると置換されない要素を元の値のままにすることができる。ただし、update()を使うと元のオブジェクトが変更され、replace()で引数inplaceTrueとするのと同じ動作となる。

s_copy = s.copy()
s_copy.update(s_copy.map({'NY': 'NewYork'}))
print(s_copy)
# 0    NewYork
# 1         CA
# 2         CA
# 3         TX
# 4         CA
# 5    NewYork
# Name: state, dtype: object

s_copy = s.copy()
s_copy.replace({'NY': 'NewYork'}, inplace=True)
print(s_copy)
# 0    NewYork
# 1         CA
# 2         CA
# 3         TX
# 4         CA
# 5    NewYork
# Name: state, dtype: object

速度比較

Jupyter Notebookの%timeitで簡易的な速度比較を行った。

すべての要素に対して置換が行われる場合はmap()のほうがreplace()よりも高速。

%timeit s.map({'NY': 'NewYork', 'CA': 'California', 'TX': 'Texas'})
# 345 µs ± 14 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit s.replace({'NY': 'NewYork', 'CA': 'California', 'TX': 'Texas'})
# 519 µs ± 15.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

update()を組み合わせるとmap()よりreplace()のほうが高速。

s_copy = s.copy()
%timeit s_copy.update(s_copy.map({'NY': 'NewYork'}))
# 643 µs ± 21.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

s_copy = s.copy()
%timeit s_copy.replace({'NY': 'NewYork'}, inplace=True)
# 230 µs ± 10 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

update()を使ってもmap()のほうが高速という報告もあり、元のpandas.Seriesのサイズ、置換を指定する辞書のサイズによって結果が異なる。

特にreplace()は置換を指定する辞書のサイズによる変化が大きい。

s_copy = s.copy()
%timeit s_copy.update(s_copy.map({'NY': 'NewYork', 'CA': 'California', 'TX': 'Texas'}))
# 627 µs ± 10.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

s_copy = s.copy()
%timeit s_copy.replace({'NY': 'NewYork', 'CA': 'California', 'TX': 'Texas'}, inplace=True)
# 441 µs ± 22.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

小さいデータの置換処理を一度するだけであればどちらでも大差ないが、巨大なデータに対して同じような置換処理を繰り返し行うといった場合は速度が効いてくるので、実際に置換を行う条件でmap()replace()を試してみてからどちらを使うか決定するといいかもしれない。

すべての要素を別の値に置換するのであれば、map()のほうが高速になる場合が多い。

速度の計測については以下の記事を参照。

map()の活用例: 質的データを量的データに変換

map()を使う例として実際によくあるのが、質的データ(カテゴリデータ)の文字列を量的データの数値に変換する場合。

機械学習のデータとして使うときなどに、例のような地名や男女の区分(male, female)などの文字列を0, 1の数値に置き換える。

s_map_num = s.map({'NY': 0, 'CA': 1, 'TX': 2})
print(s_map_num)
# 0    0
# 1    1
# 2    1
# 3    2
# 4    1
# 5    0
# Name: state, dtype: int64

map()には元のオブジェクトを更新する引数inplaceがないので、pandas.DataFrameの列をmap()で置換する場合は、元の列に代入する。

df['state'] = df['state'].map({'NY': 0, 'CA': 1, 'TX': 2})
print(df)
#       name  age  state  point
# 0    Alice   24      0     64
# 1      Bob   42      1     92
# 2  Charlie   18      1     70
# 3     Dave   68      2     70
# 4    Ellen   24      1     88
# 5    Frank   30      0     57

print(df['state'].dtype)
# int64

なお、質的データ(カテゴリ変数)をダミー変数に変換するにはpandas.get_dummies()という関数がある。詳細は以下の記事を参照。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事