DataFrameでreplaceされない要素を平均値で埋めたいけど冗長になってしまったあなたへ
はじめに
データフレームはデータ分析をPythonでやるときには必須になります。
しかし、少し複雑な処理になると冗長なコードになりがちでもっとうまい処理の仕方はないかと思います。
私の勤める企業ではPythonを利用している人がかなり少ないため、そのような処理を書くときにリファクタリング
について聞く当てがありません。(残念ながらエンジニアの知り合いも少ないです)
そんな状況でデータ分析をしているときに、「この処理うまく書けないか?」と思いました。
最近はITに関する質問をすることができるteratailやStackOverFlowなどがあり、そこでは達人が多くの質問に早く回答してくれます。
私は処理を効率よくする(調べるだけではみつけずらい)方法を学ぶために上記のサービスを利用して学ぶようにしています。(terateilのほうが回答が返ってきやすいです)
そして今回もスマートにコードが書けないかとteratailを利用しましたので、まとめたいと思います。
問題
以下のようなデータフレームがあるとします。
index | 都道府県 | 市区町村 |
---|---|---|
1 | 東京 | 渋谷 |
2 | 東京 | 新宿 |
3 | 東京 | 池袋 |
4 | 埼玉 | 大宮 |
5 | 千葉 | 新浦安 |
6 | 神奈川 | 横浜 |
そして、市区町村
を辞書(dic)を利用してreplace
します。
# データフレームの準備
pref = ["東京", "東京", "東京", "埼玉", "千葉", "神奈川"]
value = ["渋谷", "新宿", "池袋", "大宮", "新浦安", "横浜"]
df = pd.DataFrame({'都道府県':pref, '市区町村': value})
# 辞書
dic = {"渋谷":100, "新宿":90, "大宮":50, "新浦安":45, "横浜":80}
# 辞書で市区町村列を置き換え
df["市区町村"] = df["市区町村"].replace(dic)
すると、市区町村の池袋
がないために、置き換わらず以下のようなデータフレームになります。
index | 都道府県 | 市区町村 |
---|---|---|
0 | 東京 | 100 |
1 | 東京 | 90 |
2 | 東京 | 池袋 |
3 | 埼玉 | 50 |
4 | 千葉 | 45 |
5 | 神奈川 | 80 |
ここで、replace
できなかった池袋を「東京」の平均値で埋められる処理を考えます。
(100 + 90) / 2 = 95
池袋を95で埋めたいのですが、コードが冗長であったので質問をしました。
解決方法1
mapを利用することでうまく処理することができます。
replace
だと辞書にないものはそのままになりますが、map
を利用するとNaN
となるためfloat型
で扱いやすくなります。
またmapのほうが高速に動くそうです。
1.mapを利用して辞書で置換をする
import pandas as pd
# データフレームの準備
pref = ["東京", "東京", "東京", "埼玉", "千葉", "神奈川"]
value = ["渋谷", "新宿", "池袋", "大宮", "新浦安", "横浜"]
df = pd.DataFrame({'都道府県':pref, '市区町村': value})
# 辞書
dic = {"渋谷":100, "新宿":90, "大宮":50, "新浦安":45, "横浜":80}
# 置換
df["市区町村"] = df["市区町村"].map(dic)
するとdfは以下のようになります。
index | 都道府県 | 市区町村 |
---|---|---|
0 | 東京 | 100 |
1 | 東京 | 90 |
2 | 東京 | NaN |
3 | 埼玉 | 50 |
4 | 千葉 | 45 |
5 | 神奈川 | 80 |
処理速度は以下のようになり、map
の方が高速であることがわかります。
%timeit df["市区町村"].replace(dic)
# 1000 loops, best of 5: 865 µs per loop
%timeit df["市区町村"].map(dic)
# 1000 loops, best of 5: 478 µs per loop
2. 平均値で補完する
df['市区町村'] = df['市区町村'].groupby(df['都道府県']).transform(lambda s: s.fillna(s.mean()))
県ごとにまとめて、処理をしています。
apply
とtransformer
は同じような使い方になりますが、少し違いがありますので以下を参照してください。
Pandas の transform と apply の基本的な違い
そのあとfillna()
を利用してNaNに該当するところを県の平均で補完します。
index | 都道府県 | 市区町村 |
---|---|---|
0 | 東京 | 100 |
1 | 東京 | 90 |
2 | 東京 | 95 |
3 | 埼玉 | 50 |
4 | 千葉 | 45 |
5 | 神奈川 | 80 |
また1行でこの処理を書くことも可能です。
df['市区町村'] = df['市区町村'].map(dic).groupby(df['都道府県']).transform(lambda s: s.fillna(s.mean()))
解決方法2
手順を追ってやることも可能です。
1. 辞書を使って置換する
問題で行っていた手順をまず行います。
# データフレームの準備
pref = ["東京", "東京", "東京", "埼玉", "千葉", "神奈川"]
value = ["渋谷", "新宿", "池袋", "大宮", "新浦安", "横浜"]
df = pd.DataFrame({'都道府県':pref, '市区町村': value})
# 辞書
dic = {"渋谷":100, "新宿":90, "大宮":50, "新浦安":45, "横浜":80}
# 辞書で市区町村列を置き換え
df["市区町村"] = df["市区町村"].replace(dic)
index | 都道府県 | 市区町村 |
---|---|---|
0 | 東京 | 100 |
1 | 東京 | 90 |
2 | 東京 | 池袋 |
3 | 埼玉 | 50 |
4 | 千葉 | 45 |
5 | 神奈川 | 80 |
2.市区町村ごとに平均値をもとめる
# 市区町村の要素を整数に変換する
tempdf["市区町村"] = tempdf["市区町村"].astype(int)
# 市区町村ごとに平均値をもとめる
mean_df = tempdf.groupby('都道府県').mean()
index | 都道府県 | 市区町村 |
---|---|---|
0 | 東京 | 100 |
1 | 東京 | 90 |
2 | 東京 | 95 |
3 | 埼玉 | 50 |
4 | 千葉 | 45 |
5 | 神奈川 | 80 |
3. 平均値のデータフレームを辞書にする
mean_dict = mean_df.to_dict()['市区町村']
4.辞書でデータフレームを置換する
df['市区町村'] = df.apply(lambda row: row['市区町村'] if type(row['市区町村'])==int else mean_dict[row['都道府県']], axis=1)
index | 都道府県 | 市区町村 |
---|---|---|
0 | 東京 | 100 |
1 | 東京 | 90 |
2 | 東京 | 95 |
3 | 埼玉 | 50 |
4 | 千葉 | 45 |
5 | 神奈川 | 80 |
おわりに
replaceを2回利用して希望通りの結果になりました。
私は置換を2回行うところまでは同じでしたが、apply
を利用したうまい処理ができず、困っていました。
詳しい人の技術を盗むことも成長するには必要ですね。
参考文献
teratail
StackOverFlow
DataFrameのreplaceで置換されなかった要素の処理について
Pandas の transform と apply の基本的な違い
Author And Source
この問題について(DataFrameでreplaceされない要素を平均値で埋めたいけど冗長になってしまったあなたへ), 我々は、より多くの情報をここで見つけました https://qiita.com/Sicut_study/items/d3369b50228114941aa5著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .