大規模な不平衡データセットのN個の平衡ランダムサブサンプルを作成しようとしています。 scikit-learn/pandasまたはこれを自分で実装する必要がありますか?これを行うコードへのポインタはありますか?
これらのサブサンプルはランダムである必要があり、非常に大きな分類子のアンサンブルで別々の分類子にそれぞれを供給するときに重複する可能性があります。
Wekaにはspreadsubsampleというツールがありますが、sklearnには同等のものがありますか? http://wiki.pentaho.com/display/DATAMINING/SpreadSubsample
(重みについては知っていますが、それは私が探しているものではありません。)
ここに私の最初のバージョンがありますが、うまく動作しているようです、それをコピーするか、より効率的にする方法を提案してください(私はプログラミング全般でかなり長い経験がありますが、pythonでそれほど長くはありませんnumpy)
この関数は、単一のランダムバランスサブサンプルを作成します。
編集:サブサンプルのサイズは少数クラスをサンプリングするようになりました。これはおそらく変更する必要があります。
_def balanced_subsample(x,y,subsample_size=1.0):
class_xs = []
min_elems = None
for yi in np.unique(y):
elems = x[(y == yi)]
class_xs.append((yi, elems))
if min_elems == None or elems.shape[0] < min_elems:
min_elems = elems.shape[0]
use_elems = min_elems
if subsample_size < 1:
use_elems = int(min_elems*subsample_size)
xs = []
ys = []
for ci,this_xs in class_xs:
if len(this_xs) > use_elems:
np.random.shuffle(this_xs)
x_ = this_xs[:use_elems]
y_ = np.empty(use_elems)
y_.fill(ci)
xs.append(x_)
ys.append(y_)
xs = np.concatenate(xs)
ys = np.concatenate(ys)
return xs,ys
_
Pandas DataFrameを使用して上記の作業を行う場合は、いくつかの変更を加える必要があります。
_np.random.shuffle
_行を次のように置き換えます
this_xs = this_xs.reindex(np.random.permutation(this_xs.index))
_np.concatenate
_行を次のように置き換えます
xs = pd.concat(xs) ys = pd.Series(data=np.concatenate(ys),name='target')
現在、不均衡なデータに対処するための本格的なpythonパッケージが存在します。これはsklearn-contribパッケージとして https://github.com/scikit-learn-contribで入手できます。/imbalanced-learn
pandas Series :のバージョン
import numpy as np
def balanced_subsample(y, size=None):
subsample = []
if size is None:
n_smp = y.value_counts().min()
else:
n_smp = int(size / len(y.value_counts().index))
for label in y.value_counts().index:
samples = y[y == label].index.values
index_range = range(samples.shape[0])
indexes = np.random.choice(index_range, size=n_smp, replace=False)
subsample += samples[indexes].tolist()
return subsample
このタイプのデータ分割は、notで提供され、sklearn.cross_validation
。
あなたのニーズに似ていると思われるものはsklearn.cross_validation.StratifiedShuffleSplit
、データセット全体の構造を保持しながら、任意のサイズのサブサンプルを生成できます。つまり、メインデータセットにある同じ不均衡を厳密に強制します。これは探しているものではありませんが、そこにあるコードを使用して、課された比率を常に50/50に変更できる場合があります。
(これを気に入れば、おそらくscikit-learnへの非常に良い貢献になるでしょう。)
マルチクラスグループで機能する上記のコードのバージョンを次に示します(テスト済みの場合、グループ0、1、2、3、4)。
import numpy as np
def balanced_sample_maker(X, y, sample_size, random_seed=None):
""" return a balanced data set by sampling all classes with sample_size
current version is developed on assumption that the positive
class is the minority.
Parameters:
===========
X: {numpy.ndarrray}
y: {numpy.ndarray}
"""
uniq_levels = np.unique(y)
uniq_counts = {level: sum(y == level) for level in uniq_levels}
if not random_seed is None:
np.random.seed(random_seed)
# find observation index of each class levels
groupby_levels = {}
for ii, level in enumerate(uniq_levels):
obs_idx = [idx for idx, val in enumerate(y) if val == level]
groupby_levels[level] = obs_idx
# oversampling on observations of each label
balanced_copy_idx = []
for gb_level, gb_idx in groupby_levels.iteritems():
over_sample_idx = np.random.choice(gb_idx, size=sample_size, replace=True).tolist()
balanced_copy_idx+=over_sample_idx
np.random.shuffle(balanced_copy_idx)
return (X[balanced_copy_idx, :], y[balanced_copy_idx], balanced_copy_idx)
これはまた、インデックスを返すので、他のデータセットに使用したり、各データセットが使用された頻度を追跡したりできます(トレーニングに役立ちます)
私は最良の解決策を見つけました ここ
そして、これは私がそれが最も簡単だと思うものです。
dataset = pd.read_csv("data.csv")
X = dataset.iloc[:, 1:12].values
y = dataset.iloc[:, 12].values
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(return_indices=True)
X_rus, y_rus, id_rus = rus.fit_sample(X, y)
次に、X_rus、y_rusデータを使用できます
以下は私のpythonバランスのとれたデータコピーを作成するための実装です。仮定:1.ターゲット変数(y)はバイナリクラス(0対1)2. 1は少数です。
from numpy import unique
from numpy import random
def balanced_sample_maker(X, y, random_seed=None):
""" return a balanced data set by oversampling minority class
current version is developed on assumption that the positive
class is the minority.
Parameters:
===========
X: {numpy.ndarrray}
y: {numpy.ndarray}
"""
uniq_levels = unique(y)
uniq_counts = {level: sum(y == level) for level in uniq_levels}
if not random_seed is None:
random.seed(random_seed)
# find observation index of each class levels
groupby_levels = {}
for ii, level in enumerate(uniq_levels):
obs_idx = [idx for idx, val in enumerate(y) if val == level]
groupby_levels[level] = obs_idx
# oversampling on observations of positive label
sample_size = uniq_counts[0]
over_sample_idx = random.choice(groupby_levels[1], size=sample_size, replace=True).tolist()
balanced_copy_idx = groupby_levels[0] + over_sample_idx
random.shuffle(balanced_copy_idx)
return X[balanced_copy_idx, :], y[balanced_copy_idx]
次のコードを使用して、重複する各クラスの100行を選択するだけです。 activity
は私のクラス(データセットのラベル)
balanced_df=Pdf_train.groupby('activity',as_index = False,group_keys=False).apply(lambda s: s.sample(100,replace=True))
Mikkomによるトップアンサーのわずかな修正。
より大きなクラスデータの順序を保持する場合、つまりあなたはシャッフルしたくありません。
の代わりに
if len(this_xs) > use_elems:
np.random.shuffle(this_xs)
これを行う
if len(this_xs) > use_elems:
ratio = len(this_xs) / use_elems
this_xs = this_xs[::ratio]
私のサブサンプラーバージョン、これが役立つことを願って
def subsample_indices(y, size):
indices = {}
target_values = set(y_train)
for t in target_values:
indices[t] = [i for i in range(len(y)) if y[i] == t]
min_len = min(size, min([len(indices[t]) for t in indices]))
for t in indices:
if len(indices[t]) > min_len:
indices[t] = random.sample(indices[t], min_len)
return indices
x = [1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, -1]
j = subsample_indices(x, 2)
print j
print [x[t] for t in j[-1]]
print [x[t] for t in j[1]]
既存のsklearnパイプラインに緊密に統合できる私のソリューションは次のとおりです。
from sklearn.model_selection import RepeatedKFold
import numpy as np
class DownsampledRepeatedKFold(RepeatedKFold):
def split(self, X, y=None, groups=None):
for i in range(self.n_repeats):
np.random.seed()
# get index of major class (negative)
idxs_class0 = np.argwhere(y == 0).ravel()
# get index of minor class (positive)
idxs_class1 = np.argwhere(y == 1).ravel()
# get length of minor class
len_minor = len(idxs_class1)
# subsample of major class of size minor class
idxs_class0_downsampled = np.random.choice(idxs_class0, size=len_minor)
original_indx_downsampled = np.hstack((idxs_class0_downsampled, idxs_class1))
np.random.shuffle(original_indx_downsampled)
splits = list(self.cv(n_splits=self.n_splits, shuffle=True).split(original_indx_downsampled))
for train_index, test_index in splits:
yield original_indx_downsampled[train_index], original_indx_downsampled[test_index]
def __init__(self, n_splits=5, n_repeats=10, random_state=None):
self.n_splits = n_splits
super(DownsampledRepeatedKFold, self).__init__(
n_splits=n_splits, n_repeats=n_repeats, random_state=random_state
)
通常どおり使用します。
for train_index, test_index in DownsampledRepeatedKFold(n_splits=5, n_repeats=10).split(X, y):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
すでに回答されていますが、似たようなものを探しているあなたの質問に私はつまずきました。さらに調査した結果、_sklearn.model_selection.StratifiedKFold
_をこの目的に使用できると思います。
_from sklearn.model_selection import StratifiedKFold
X = samples_array
y = classes_array # subsamples will be stratified according to y
n = desired_number_of_subsamples
skf = StratifiedKFold(n, shuffle = True)
batches = []
for _, batch in skf.split(X, y):
do_something(X[batch], y[batch])
_
__
_を追加することは重要です。なぜなら、skf.split()
はK分割交差検証の成層化された折り畳みを作成するために使用されるため、インデックスの2つのリストを返すためです:train
(_n - 1 / n
_要素)およびテスト(_1 / n
_要素)。
これは sklearn 0.18 のものであることに注意してください。 sklearn 0.17 では、同じ関数がモジュール_cross_validation
_にあります。
pandas DataFrameサブサンプリング(uspl=True
)またはオーバーサンプリング(uspl=False
)、2つ以上の値を持つデータフレーム内の指定された列によってバランスが取られます。
ために uspl=True
、このコードはランダムなサンプルを取ります置換なしすべての層からの最小層に等しいサイズの。ために uspl=False
、このコードは、すべての階層から最大の階層に等しいサイズのランダムサンプル置換ありを取ります。
def balanced_spl_by(df, lblcol, uspl=True):
datas_l = [ df[df[lblcol]==l].copy() for l in list(set(df[lblcol].values)) ]
lsz = [f.shape[0] for f in datas_l ]
return pd.concat([f.sample(n = (min(lsz) if uspl else max(lsz)), replace = (not uspl)).copy() for f in datas_l ], axis=0 ).sample(frac=1)
これはPandas DataFrameでのみ機能しますが、それは一般的なアプリケーションのようです。これをPandas DataFramesに制限すると、告げられる。