web-dev-qa-db-ja.com

Scikit-learnバランスサブサンプリング

大規模な不平衡データセットのN個の平衡ランダムサブサンプルを作成しようとしています。 scikit-learn/pandasまたはこれを自分で実装する必要がありますか?これを行うコードへのポインタはありますか?

これらのサブサンプルはランダムである必要があり、非常に大きな分類子のアンサンブルで別々の分類子にそれぞれを供給するときに重複する可能性があります。

Wekaにはspreadsubsampleというツールがありますが、sklearnには同等のものがありますか? http://wiki.pentaho.com/display/DATAMINING/SpreadSubsample

(重みについては知っていますが、それは私が探しているものではありません。)

39
mikkom

ここに私の最初のバージョンがありますが、うまく動作しているようです、それをコピーするか、より効率的にする方法を提案してください(私はプログラミング全般でかなり長い経験がありますが、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を使用して上記の作業を行う場合は、いくつかの変更を加える必要があります。

  1. _np.random.shuffle_行を次のように置き換えます

    this_xs = this_xs.reindex(np.random.permutation(this_xs.index))

  2. _np.concatenate_行を次のように置き換えます

    xs = pd.concat(xs) ys = pd.Series(data=np.concatenate(ys),name='target')

23
mikkom

現在、不均衡なデータに対処するための本格的なpythonパッケージが存在します。これはsklearn-contribパッケージとして https://github.com/scikit-learn-contribで入手できます。/imbalanced-learn

21
eickenberg

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
8
gc5

このタイプのデータ分割は、notで提供され、sklearn.cross_validation

あなたのニーズに似ていると思われるものはsklearn.cross_validation.StratifiedShuffleSplit、データセット全体の構造を保持しながら、任意のサイズのサブサンプルを生成できます。つまり、メインデータセットにある同じ不均衡を厳密に強制します。これは探しているものではありませんが、そこにあるコードを使用して、課された比率を常に50/50に変更できる場合があります。

(これを気に入れば、おそらくscikit-learnへの非常に良い貢献になるでしょう。)

3
eickenberg

マルチクラスグループで機能する上記のコードのバージョンを次に示します(テスト済みの場合、グループ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)

これはまた、インデックスを返すので、他のデータセットに使用したり、各データセットが使用された頻度を追跡したりできます(トレーニングに役立ちます)

3
Kevin Mader

私は最良の解決策を見つけました ここ

そして、これは私がそれが最も簡単だと思うものです。

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データを使用できます

2
Lin Feng

以下は私の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]
2
beingzy

次のコードを使用して、重複する各クラスの100行を選択するだけです。 activityは私のクラス(データセットのラベル)

balanced_df=Pdf_train.groupby('activity',as_index = False,group_keys=False).apply(lambda s: s.sample(100,replace=True))
1
javac

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]
1
Bert Kellerman

私のサブサンプラーバージョン、これが役立つことを願って

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]]
0
hernan

既存の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]
0

すでに回答されていますが、似たようなものを探しているあなたの質問に私はつまずきました。さらに調査した結果、_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_にあります。

0
kadu

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に制限すると、告げられる。

0
Roko Mijic