このページのアンダーサンプリングの章にあるコードを改変して、実際に動くものにしました。
# trainデータの陽性データと陰性データの数が同じになるようにするためのメソッド
# 参考 : https://qiita.com/ryouta0506/items/619d9ac0d80f8c0aed92
# kmeansでクラスタにして、クラスタごとに一定の割合でサンプルする
# X : pandas の DataFrame
# target_column_name : クラス名。 「発症フラグ」 など。
# minority_label : 少数ラベルの値。 「1」 など。
def under_sampling(X, target_column_name, minority_label):
# 毎回出るので非表示に
import warnings
warnings.simplefilter('ignore', pd.core.common.SettingWithCopyWarning)
# majority と minority に分ける
X_majority = X.query(f'{target_column_name} != {minority_label}')
X_minority = X.query(f'{target_column_name} == {minority_label}')
# KMeansでクラスタリング
from sklearn.cluster import KMeans
km = KMeans(random_state=43)
km.fit(X_majority)
X_majority['Cluster'] = km.predict(X_majority)
# クラスタごとに何サンプル抽出するか計算
ratio = X_majority['Cluster'].value_counts() / X_majority.shape[0]
n_sample_ary = (ratio * X_minority.shape[0]).astype('int64').sort_index()
# クラスタごとにサンプルを抽出
dfs = []
for i, n_sample in enumerate(n_sample_ary):
dfs.append(X_majority.query(f'Cluster == {i}').sample(n_sample))
# minority データも結合するようにしておく
dfs.append(X_minority)
# アンダーサンプリング後のデータを作成
X_new = pd.concat(dfs, sort=True)
# 不要なので削除
X_new = X_new.drop('Cluster', axis=1)
return X_new