いろいろ倉庫

KNIME、EXCEL、R、Pythonなどの備忘録

【Python】scikit-learnで欠損値を補完したい。

・お題:欠損値を補完したい。

 

・前回、主にpandasを用いて欠損値を数えたり削除したり何らかの定数で補完する方法を少し調べた。

・今回は、scikit-learnでもうちょっと頑張って欠損値を補完したい。

・とりあえず、データセットを作る。

import pandas as pd
import numpy as np
import random

 

a=np.random.rand(1000)
b=[i + np.random.normal(0,0.1) for i in a]
c=[i*(-1.3) + np.random.normal(0,0.2) for i in a]
df=pd.DataFrame({"A":a,"B":b,"C":c})

・dfの中身は以下の通り。Aは0-1の乱数。BはAに正規分布の乱数を加えた数。CはAを-1.3倍して正規分布の乱数を加えた数。それぞれ1000個ずつ数字が入っている。

・次に、欠損を持つデータフレームdf2を作成する。

df2=df.copy()
mask=np.random.choice([0,1],p=[0.1,0.9],size=df2.shape[0]*df2.shape[1]).reshape(df2.shape[0],df2.shape[1]).astype(bool)
df2=df2.where(mask, np.nan)
df2.info()

出力は、

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   A       899 non-null    float64
 1   B       897 non-null    float64
 2   C       903 non-null    float64
dtypes: float64(3)
memory usage: 23.6 KB

になる。どの列も1割程度を欠損している。

 

・scikit-learnを用いた補完は以下の記事を参考にさせて頂いた。詳しいことはリンク先の記事をご参照いただきたい。

qiita.com

#デフォルトで補完してみる
from sklearn.experimental import enable_iterative_imputer#これがないと下のコマンドが走らなかった。
from sklearn.impute import IterativeImputer
df2_DefHokan = pd.DataFrame(IterativeImputer().fit_transform(df2))
df2_DefHokan.info()

出力は以下。

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   0       1000 non-null   float64
 1   1       1000 non-null   float64
 2   2       1000 non-null   float64
dtypes: float64(3)
memory usage: 23.6 KB

non-nullが1000個なので、補完されたっぽい。

 

#k-nnで補完してみる。
from sklearn.impute import KNNImputer
df2_KnnHokan = pd.DataFrame(KNNImputer(n_neighbors=3).fit_transform(df2))

 

#ランダムフォレスト回帰で補完してみる。
from sklearn.ensemble import RandomForestRegressor
df2_RfHokan=pd.DataFrame(IterativeImputer(RandomForestRegressor()).fit_transform(df2))

 

・ここまででそれぞれの手法で欠損値を補完したデータフレームを作成したことになる。実際どんな感じで補完されたのか、ちょっと見てみたい。以降の処理で、欠損させる前のデータ(df)の補完対象となったデータと、それぞれの手法で補完されたデータを突き合わせ、どんな感じか見てみる。

Kesson=df2.isna()

#とりあえず列Aの変数がどう補完されたか見てみたい。A列を取り出して、一つのDataFrameにまとめて、seabornのpairplotに投げる。

ori_A=df.iloc[:,0][Kesson.iloc[:,0]]
DefHokan_A=df2_DefHokan.iloc[:,0][Kesson.iloc[:,0]]
KnnHokan_A=df2_KnnHokan.iloc[:,0][Kesson.iloc[:,0]]
RfHokan_A=df2_RfHokan.iloc[:,0][Kesson.iloc[:,0]]

Hokan_A=pd.DataFrame({"ori_A":ori_A,"DefHokan_A":DefHokan_A,"KnnHokan_A":KnnHokan_A,"RfHokan_A":RfHokan_A})

 

import seaborn as sns
sns.pairplot(Hokan_A)

・ori_Aとそれぞれの手法で補完したAを散布図にプロットすると、結構うまいこと補完できていそうだった。また、Hokanどうしを見比べてみると、oriと比較した場合と比べて分布が締まって見える。

・BとCに関しては割愛。

 

おわり。

 

【Python】欠損値を何とかしたい

・お題:データセット中の欠損値の有無などを調べて、何とかしたい。

 

・入手したデータが、端から端までピッチリ埋まっていることは、実験データを収集するうえで期待できない場合が多い。入手したデータセットの欠損値に関して情報を得て、何とかしたい。

・とりあえず、データセットを作る。

import pandas as pd
import numpy as np
import random

a=np.random.rand(15)
b=[i + np.random.normal(0,0.1) for i in a]
df=pd.DataFrame({"A":a,"B":b})

#dfはA列に15個、B列に15個データがピッチリ入ったデータセット。ここから欠損を含むデータセットdf2を作成する。

df2=df.copy()
mask=np.random.choice([0,1],p=[0.2,0.8],size=df2.shape[0]*df2.shape[1]).reshape(df2.shape[0],df2.shape[1]).astype(bool)#2割程度を欠損値にしたい。
df2=df2.where(mask, np.nan)

df2["C"]=[i*(-1.3) + np.random.normal(0,0.2) for i in a]#さらに、欠損値を含まない15個のデータが入ったC列を追加した。

・df2の中身は以下の通り。

・どこに何個データが入っているか知りたい。

df2.info()

を実行すると、

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 15 entries, 0 to 14
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   A       11 non-null     float64
 1   B       12 non-null     float64
 2   C       15 non-null     float64
dtypes: float64(3)
memory usage: 488.0 bytes

が返ってくる。15の行があり、A列には11個、B列には12個、C列には15個ののfloat64が入っている。

・どこに欠損値があるのか、何個欠損値があるのか知りたい。

df2.isna()

すると、

が返ってくる。df2.isnull()でも同じ結果になる。df2.notna()だとTrueとFalseが逆になる。~で反転させても良いので、この辺は気分だと思う。

どの列に何個欠損値があるのか調べたい場合、

df2.isna().sum()

すると

A    4
B    3
C    0
dtype: int64

が返ってくる。Trueが1でFalseが0なので、sumすればTrueを数えることになるっぽい。今回は欠損値がTrueなので、欠損値の数が返ってくる。欠損値が入っているのかどうかを調べたい場合、df2.isna().any()すれば、欠損値のある列はTrue、欠損値のない列はFalseが返ってくる。

・欠損値の入った行を消したい。

mask=df2.isna().sum(axis=1).astype(bool)#1つでも欠損値があるとTrueになる。

df2[-mask]

で、欠損値が一つでもある行が取り除かれ、以下が返ってくる。

わざわざこんなことしなくても、df2.dropna()で欠損値の入った行を取り除くことができる。また、引数にaxis=1を入れれば欠損値の入った行を除去でき、thresh=を入れれば除外する行の欠損値の閾値を設定できる。便利。

・欠損値を何かで埋めたい。

df2.fillna(引数)

で、欠損値を引数で埋めることができる。

この引数には辞書を入れることもでき、列名をキーとして辞書の対応する値を挿入できる

dic={"A":1,"B":10,"c":100}
df2.fillna(dic)

とすると、

が返ってくる。

前後の数の算術平均値で埋める場合は、

df2.interpolate()

とすればいいかが返ってくる。

A列の1行と2行は続きの欠損値だったが、さらにその上下の数の等差数列のようになった。順序をもとに埋められそうな変数なら、このような埋め方も有効なのかもしれない。

 

・欠損値の有無や数を調べることは当然必要だが、難しいのはそれを知ってどう対処すべきなんだろうなぁ、と感じた。

 

おわり。

 

 

 

【KNIME】構造情報を一覧で見やすくしたい

・先日、Tile Viewで情報を見やすく成型したが、構造情報をうまく表示することができなかった。

・Tile Viewは対応するセルの情報をカードに出力してくれるのだから、画像データをそのまま渡せばよいのでは?と思って調べたところ、SMILESの構造情報を画像として出力することができるノードがあるらしい(以下の参考の74ページあたりに出てくるRenderer to Imageノード)。

Hands-on: Data preparation and interactive visualization of chemical structures in KNIME
Analytics Platform
AIDD April 19th, 2022
Daria Goldmann KNIME AG

https://ai-dd.eu/sites/default/files/online/Daria_Goldmann_KNIME.pdf

・これを、Tile Viewで表示すれば良いかもしれない。

・ということで、ワークフローを組んでみた(以下)。

・Renderer to Imageノードが、SMILESを画像に変換してくれる(以下、設定と出力)。

・これをTile Viewに渡すと、それっぽく表示してくれた(下図)。

・いろいろな変数を与えることで、構造と変数を一緒にカード形式で表示することもできそう。

 

おわり