🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

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

Q&A

解決済

1回答

1602閲覧

multiple imputation について

SyunSyun

総合スコア24

Python

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

0グッド

2クリップ

投稿2019/10/07 13:46

Pythonでのmultiple imputationについての質問です。
性別(男女)、年齢(0-105才)、身長(40-200㎝)、体重(3-200㎏)の4変数の10000サンプルあるデータフレーム(df1)に関して、性別以外にところどころNan(10%以下)があります。
これらをMultiple imputationを使って補完しようとしました。
プログラムは以下です。

from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imp = IterativeImputer(max_iter=10, sample_posterior=True)

X = df1.iloc[:, 0:4]
y = df1.iloc[:, 0:4]

imp.fit(X)
a = np.round(imp.transform(y))
feature = df1.columns[0:4]
df2 = pd.DataFrame(data=a ,columns= feature)

df2

このようにすると、Imputationの設定は
IterativeImputer(add_indicator=False, estimator=None,
imputation_order='ascending', initial_strategy='mean',
max_iter=10, max_value=None, min_value=None,
missing_values=nan, n_nearest_features=None, random_state=None,
sample_posterior=True, tol=0.001, verbose=0)
と出ます。
特にエラーなくdf2としてNanがimputeされたデータフレームが返されますが、問題は0才なのに身長160㎝、体重60㎏などと現実的でない数値がimputeされてしまっています。
確かに身長と体重の変数の平均はそれぞれ、そのくらいなのですが、
実際の想定範囲内の身長50-60㎝、体重6㎏などくらいにimputeされていてほしいのです。
(実際、データセットには0歳児の欠損してないデータも60サンプルくらい入っている)

Multiple imputationの原理のためなのか、私のプログラミングミスのせいなのか判断できなくて困っています。

Multiple imputationに詳しい方で、この問題点に関してご意見聞かせて頂けるとありがたいです。
さらにこのデータセットの場合補完法で適していると思われる手法があれば、ご意見くださるとありがたいです。

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

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

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

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

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

hayataka2049

2019/10/07 16:54

ダミーとか似ているものでも構わないので、同じような状況を再現できるデータセットを提供していただけませんか? 検証してみたいのですが、データで苦労するので……
hayataka2049

2019/10/08 05:26

ファイルを確認できました。今日中くらいに試してみます。
SyunSyun

2019/10/08 14:30

有難うございます。 もし、何かわかることがあったらまた教えてください。 個人的にはプログラミングの問題というより、Mmultiple imputationの原理のためと思うのですが。
hayataka2049

2019/10/08 14:36

今回答書いてますが、まあその通りです。すぐに投げます。
guest

回答1

0

ベストアンサー

まず、4つしか変数がないのに一行で2つの欠損があるようなデータは悪条件ですから、ある意味では多少の妥協は必要になります。


次に、max_iterを増やしたり、max_value, min_valueを指定したりすることで多少の改善が見込めます。そんなに大げさな改善にはならないと思いますが。


最後に、これが一番深刻な問題ですが、IterativeImputerが内部で用いている回帰モデルはベイジアンリッジで、線形モデルになります。性別・年齢・身長・体重の関係が線形ということは考えづらいので、予測に限界があります。

こういうとき、非線形回帰系のモデルを手軽に突っ込めると助かるのですが、IterativeImputerで使える回帰モデルには

If sample_posterior is True, the estimator must support return_std in its predict method.
sklearn.impute.IterativeImputer — scikit-learn 0.21.3 documentation

というきつい制約があり、条件を満たす非線形回帰手法のモデルは軽く探した範囲だとGaussianProcessRegressorくらいしかありません。やってみましたがチューニングがよくわからないし、すぐ警告を吐くのでちょっと難があります。ちゃんと使えばたぶんできます。

一例ですが、私が使ってなんとかまともにうごいたかなという気がした定義です。パラメータの妥当性は保証しません。

python

1# 追加で必要になるimportのみ記載。以下のコードでも同じ 2from sklearn.preprocessing import StandardScaler 3from sklearn.gaussian_process import GaussianProcessRegressor 4from sklearn.pipeline import Pipeline 5 6ss = StandardScaler() 7gp = GaussianProcessRegressor(alpha=1e-5, normalize_y=True) 8est = Pipeline([("scaler", ss), ("reg", gp)]) 9imp = IterativeImputer(est, max_iter=10, sample_posterior=True)

multiple imputationを諦めればSVRだろうがランダムフォレストだろうが使えるので、それはそれで楽ではあります。

python

1from sklearn.ensemble import RandomForestRegressor 2 3est = RandomForestRegressor(n_estimators=100, n_jobs=-1) 4imp = IterativeImputer(est, max_iter=10, sample_posterior=False)

理論的な良し悪しはともかく、こっちの方が良い結果が得られるなら採用するというのもありだと思います。


他の方法は、非線形回帰ができるように然るべき変換をするTransformerを作ってやることです。

どう変換するかというと、選択肢の一つは多項式回帰にして無理やり解くことです。やるとたしかにできますが、しょせん多項式なのでちょっと当てはまりが悪いかもしれません。試しましたが、年齢で100歳とか出ちゃったりして、愉快な感じでした。

python

1from sklearn.preprocessing import PolynomialFeatures, StandardScaler 2from sklearn.linear_model import BayesianRidge 3from sklearn.pipeline import Pipeline 4 5ss = StandardScaler() 6pf = PolynomialFeatures(degree=3, include_bias=False) 7br = BayesianRidge() 8est = Pipeline([("scaler", ss), ("poly_f", pf), ("reg", br)]) 9imp = IterativeImputer(est, max_iter=10, sample_posterior=True)

もう少し正攻法で考えると、とりあえず身長体重は大雑把に言えば線形とみなしてよさそうで、性別は0か1ですからどうでもよく、加齢に対する成長の効果を適切に反映するような変換だけすればなんとかなる、という発想があり得るかもしれません。ということで、適当に作ったのがこれです。

python

1from sklearn.preprocessing import FunctionTransformer 2from sklearn.compose import ColumnTransformer 3from sklearn.linear_model import BayesianRidge 4 5def trans(x): 6 """ここ見ながら適当に作りました↓ 7 https://www.graphsketch.com/ 8 """ 9 return 20-np.exp(-0.3*x+3) 10 11ft = FunctionTransformer(trans) 12ct = ColumnTransformer([("age", ft, [1])], remainder="passthrough") 13br = BayesianRidge() 14est = Pipeline([("scaler", ct), ("reg", br)]) 15imp = IterativeImputer(est, max_iter=10, sample_posterior=False) 16

だいたい20歳以上で成長が止まるという状況を想定した関数を作ってやってますが・・・だいたいそれっぽくはなったけど、もう少し工夫が必要な気もしました。

投稿2019/10/08 15:04

hayataka2049

総合スコア30935

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

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

SyunSyun

2019/10/08 17:25

hayataka2049様。大変丁寧な解説を有難うございました。 うまくいかない最大の理由は、リグレッサーが線形回帰になっていたことなんですね。 教えて頂いた事をもとに自分なりにさらに、予測性能があがるものに改良したいと思います。 色んな手段を教えて頂いて、感動しました。 これからも地道に頑張ります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問