静かなる名辞

pythonとプログラミングのこと



【python】numpyでデータをランダムサンプリング

 機械学習に使うデータをランダムサンプリングしたいときがある。簡単そうなのにやり方が見つからないから自分で書く。

実装方針

重複ありランダムサンプリング

 これはnp.random.randintでn個のindexを作り、Fancy Indexingするだけ。

重複なしランダムサンプリング

 ちょっと面倒くさいというかやり方が幾つか思いつくが、とりあえず今回方針としては、

  1. オリジナルデータのindexの配列を作る
  2. shuffleする
  3. 先頭からn個取り出してsample_indexとする
  4. そのsample_indexを使ってオリジナルデータに対してFancy Indexingし、取り出す

 これで行くことにする。

実装と結果

 以上を踏まえて、次のようなソースを書いた。

# coding: UTF-8

import numpy as np
from sklearn.datasets import load_iris

def sampling_Xy(X, y, sample_n=50, dup=False):
    n_data = X.shape[0]

    if dup:
        samples_index = np.random.randint(0, n_data, sample_n)
    else:
        original_index = np.arange(n_data)
        np.random.shuffle(original_index)
        samples_index = original_index[:sample_n]
    return X[samples_index], y[samples_index], samples_index

def main():
    iris = load_iris()
    print(iris.data.shape, iris.target.shape)
    sample_X, sample_y, samples_index= sampling_Xy(
        iris.data, iris.target, sample_n=80, dup=False)
    print(sample_X.shape, sample_X.shape, len(set(samples_index)))
    sample_X, sample_y, samples_index = sampling_Xy(
        iris.data, iris.target, sample_n=80, dup=True)
    print(sample_X.shape, sample_X.shape, len(set(samples_index)))

if __name__ == "__main__":
    main()

 結果は、

(150, 4) (150,)
(80, 4) (80, 4) 80
(80, 4) (80, 4) 59

 となり正しくランダムサンプリングできていることが確認できた。

そもそもなにに使いたかったの?

 手軽に実験を回してフィードバックを得て改良していくにはでかすぎるデータセットとか重すぎるアルゴリズムが世の中にはたくさんあるので、そういうのに対して使いたかったんです・・・。

裏技

 この方法だと教師ラベルの比率にまでは気を遣っていないので、特に少ないデータ数では問題(特定クラスに偏る)が発生する可能性がある。

 手軽に回避する方法として、StratifiedKFold.splitの返り値の先頭の先頭だけ使うことが考えられる。本来の使い方ではないけど、コード量は圧縮できそう。

 ただし、元データの教師ラベルの比率が均等な保証はないので、厳密に元データと同じ比率でサンプリングしたい・・・とか考え出すと無駄だし、そこまでやる意義も少ない。けっきょく、ランダムサンプリングで良いじゃん、ということになる。

※追記(参照することをおすすめします)

 numpyでできるもっと良い方法を見つけました。

 こっちを使うと、自分で書く量を減らして簡単にできそうです。

www.haya-programming.com