質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1868閲覧

DataFrameのreplaceで置換されなかった要素の処理について

WatanabeJin

総合スコア44

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/05/28 08:01

DataFrameのreplaceをした際に、replaceされない要素について以下の処理を行いたいです。

以下のようなDataFrameがあります。

pref = ["東京", "東京", "東京", "埼玉", "千葉", "神奈川"] value = ["渋谷", "新宿", "池袋", "大宮", "新浦安", "横浜"] df = pd.DataFrame({'都道府県':pref, '市区町村': value})
index都道府県市区町村
1東京渋谷
2東京新宿
3東京池袋
4埼玉大宮
5千葉新浦安
6神奈川横浜

このDataFrameの市区町村をkeyにして、replaceを利用してvalueに置換します。

dic = {"渋谷":100, "新宿":90, "大宮":50, "新浦安":45, "横浜":80} df["市区町村"] = df["市区町村"].replace(dic)

すると以下のようなDataFrameができます。

index都道府県市区町村
1東京100
2東京90
3東京池袋
4埼玉50
5千葉45
6神奈川80

ここで、辞書のkeyにない池袋には東京の平均値(100+90)/2=95を入れたいです。

しかし、うまく処理をする方法がわかりませんでした。

できれば、{"池袋":95}の辞書を再度定義してreplaceするなどはしたくないです。(たくさんの市区町村が本来はあるため、辞書を作成するのが大変です)

うまいこと処理できるような方法はありますでしょうか。よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

解決済になっていますが、ベストアンサーとなっている回答が適当ではないので投稿します。


まず、辞書を用いて値を置換する際に、質問文では.replace()メソッドを用いていますが、代わりに.map()メソッドを用いると辞書のキーに存在せず置換されなかった値は自動的にNaNに変換されます。

python

1In [11]: df["市区町村"].replace(dic) 2Out[11]: 30 100 41 90 52 池袋 63 50 74 45 85 80 9Name: 市区町村, dtype: object 10 11In [12]: df["市区町村"].map(dic) 12Out[12]: 130 100.0 141 90.0 152 NaN 163 50.0 174 45.0 185 80.0 19Name: 市区町村, dtype: float64

その結果、.replace()メソッドを用いて得られたシリーズは整数型と文字列型が混在するobjectデータ型になってしまっていますが、.map()メソッドを用いて得られたシリーズはすべてが数値からなるfloatデータ型になっています(なお整数型になっていないのはintデータ型がNaNを受け入れないためです)。
さらに、このとき.replace()メソッドよりも.map()メソッドのほうが高速に動作していることも注目に値します(この速度差はシリーズの行数が多いとさらに開きます)。

python

1In [13]: %timeit df["市区町村"].replace(dic) 2 ...: %timeit df["市区町村"].map(dic) 3694 µs ± 8.52 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 4475 µs ± 9.99 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

つづいて、値がNaNとなっているデータを特定の値で上書きする場合には、.fillna()メソッドが利用できます。.map()メソッドを用いたことがここで生きてきます。NaNに変換したことで、わざわざ'池袋'をキーにした辞書ライクなものを作成する必要がなくなり、シリーズの標準のメソッドを活用することができます。

シリーズの平均値で欠損値を穴埋めする場合は、Series.fillna(Series.mean))のようにします。以下はこの動作の例です。

python

1In [14]: s = pd.Series([1, 2, None, 3, 4]) 2 3In [15]: s.fillna(s.mean()) 4Out[15]: 50 1.0 61 2.0 72 2.5 83 3.0 94 4.0 10dtype: float64

この動作を'都道府県'列の値に基づいたグループごとに適用します。「シリーズを変更する動作を、グループごとに適用」は.groupby().transform()を用います(.groupby().apply()でも可能ですが適当ではありません)。

python

1In [16]: df['市区町村'].map(dic).groupby(df['都道府県']).transform(lambda s: s.fillna(s.mean())) 2Out[16]: 30 100.0 41 90.0 52 95.0 63 50.0 74 45.0 85 80.0 9Name: 市区町村, dtype: float64

このようにして得たシリーズをもとのdfに再び割り当てることで、求めたい結果が得られます。


以下に今回の操作をまとめます。

python

1In [17]: pref = ["東京", "東京", "東京", "埼玉", "千葉", "神奈川"] 2 ...: value = ["渋谷", "新宿", "池袋", "大宮", "新浦安", "横浜"] 3 ...: df = pd.DataFrame({'都道府県':pref, '市区町村': value}) 4 ...: dic = {"渋谷":100, "新宿":90, "大宮":50, "新浦安":45, "横浜":80} 5 ...: 6 ...: df 7Out[17]: 8 都道府県 市区町村 90 東京 渋谷 101 東京 新宿 112 東京 池袋 123 埼玉 大宮 134 千葉 新浦安 145 神奈川 横浜 15 16In [18]: df.assign(市区町村=df['市区町村'].map(dic).groupby(df['都道府県']).transform(lambda s: s.fillna(s.mean()))) 17Out[18]: 18 都道府県 市区町村 190 東京 100.0 201 東京 90.0 212 東京 95.0 223 埼玉 50.0 234 千葉 45.0 245 神奈川 80.0

投稿2021/06/02 04:02

編集2021/06/02 04:08
kirara0048

総合スコア1399

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

WatanabeJin

2021/06/02 04:29

このような回答お待ちしておりました!ありがとうございます。
guest

0

そういう特殊な処理を簡単にはできませんので、手順を追って普通に行いましょう。

python

1>>> import pandas as pd 2>>> 3>>> pref = ["東京", "東京", "東京", "埼玉", "千葉", "神奈川"] 4>>> value = ["渋谷", "新宿", "池袋", "大宮", "新浦安", "横浜"] 5>>> df = pd.DataFrame({'都道府県':pref, '市区町村': value}) 6>>> dic = {"渋谷":100, "新宿":90, "大宮":50, "新浦安":45, "横浜":80} 7>>> df["市区町村"] = df["市区町村"].replace(dic) 8>>> tempdf = df[df["市区町村"].apply(lambda x:type(x) == int)].copy() 9>>> tempdf["市区町村"] = tempdf["市区町村"].astype(int) 10>>> mean_df = tempdf.groupby('都道府県').mean() 11>>> mean_dict = mean_df.to_dict()['市区町村'] 12>>> df['市区町村'] = df.apply(lambda row: row['市区町村'] if type(row['市区町村'])==int else mean_dict[row['都道府県']], axis=1) 13>>> print(df) 14 都道府県 市区町村 150 東京 100 161 東京 90 172 東京 95 183 埼玉 50 194 千葉 45 205 神奈川 80

投稿2021/05/28 09:18

ppaul

総合スコア24670

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

WatanabeJin

2021/05/28 09:27

とてもスマートなやり方です!かなり遠回りでやっていましたので助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問