webentwicklung-frage-antwort-db.com.de

Scikit-Learn balanciertes Subsampling

Ich versuche, N ausgeglichene zufällige Subsamples meines großen, unausgeglichenen Datensatzes zu erstellen. Gibt es eine Möglichkeit, dies einfach mit scikit-learn/pandas zu tun, oder muss ich es selbst implementieren? Irgendwelche Hinweise, um das zu tun?

Diese Subsamples sollten zufällig sein und sich überlappen, wenn ich sie mit einem separaten Klassifizierer in einem sehr großen Ensemble von Klassifizierern füge.

In Weka gibt es ein Werkzeug namens Spreadsubsample. Gibt es in sklearn ein gleichwertiges Werkzeug? http://wiki.pentaho.com/display/DATAMINING/SpreadSubsample

(Ich weiß über Gewichtung, aber das ist nicht das, wonach ich suche.)

34
mikkom

Es gibt jetzt ein ausgewachsenes Python-Paket, um unausgewogene Daten anzusprechen. Es ist als Sklearn-Contrib-Paket verfügbar unter https://github.com/scikit-learn-contrib/imbalanced-learn

20
eickenberg

Eine Version für Pandas Serie :

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

Diese Art der Datenaufteilung ist nicht unter den in sklearn.cross_validation.

Was Ihren Bedürfnissen ähnlich zu sein scheint, ist sklearn.cross_validation.StratifiedShuffleSplit, mit dem Unterproben beliebiger Größe generiert werden können, während die Struktur des gesamten Datensatzes beibehalten wird, d. h. akribisch dasselbe Ungleichgewicht erzwungen wird, das sich in Ihrem Hauptdatensatz befindet. Dies ist zwar nicht das, wonach Sie suchen, aber möglicherweise können Sie den darin enthaltenen Code verwenden und das auferlegte Verhältnis immer auf 50/50 ändern.

(Dies wäre wahrscheinlich ein sehr guter Beitrag zum Scikit-Lernen, wenn Sie dazu bereit sind.)

3
eickenberg

Ich habe die besten Lösungen gefunden hier

Und ich denke, das ist das einfachste.

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)

dann können Sie X_rus, y_rus Daten verwenden

2
Lin Feng

Hier ist eine Version des obigen Codes, die für Multiklassengruppen geeignet ist (in meiner getesteten Fallgruppe 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)

Dadurch werden auch die Indizes zurückgegeben, sodass sie für andere Datensätze verwendet werden können und um zu verfolgen, wie häufig jeder Datensatz verwendet wurde (hilfreich für das Training).

2
Kevin Mader

Im Folgenden finden Sie meine Python-Implementierung zum Erstellen einer ausgeglichenen Datenkopie. Annahmen: 1. Zielvariable (y) ist binäre Klasse (0 vs. 1) 2. 1 ist die Minderheit.

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

Eine leichte Modifikation der Top-Antwort von mikkom.

Wenn Sie die Reihenfolge der größeren Klassendaten beibehalten möchten, d. du willst nicht mischen.

Anstatt

    if len(this_xs) > use_elems:
        np.random.shuffle(this_xs)

mach das

        if len(this_xs) > use_elems:
            ratio = len(this_xs) / use_elems
            this_xs = this_xs[::ratio]
1
Bert Kellerman

Wählen Sie einfach 100 Zeilen in jeder Klasse mit Duplikaten aus, indem Sie folgenden Code verwenden. activity ist meine Klassen (Labels des Datensatzes) 

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

Meine Subsampler-Version, hoffe das hilft

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

Hier ist meine Lösung, die eng in eine bestehende sklearn-Pipeline integriert werden kann:

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
    )

Verwenden Sie es wie gewohnt:

    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

Obwohl es bereits beantwortet wurde, bin ich auf Ihre Frage gestoßen und habe nach etwas Ähnlichem gesucht. Nach einiger Recherche glaube ich, dass sklearn.model_selection.StratifiedKFold für diesen Zweck verwendet werden kann:

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])

Es ist wichtig, dass Sie _ hinzufügen. Da skf.split() zum Erstellen von geschichteten Falten für die K-fach Kreuzvalidierung verwendet wird, werden zwei Indexlisten zurückgegeben: train (n - 1 / n-Elemente) und test (1 / n-Elemente).

Bitte beachten Sie, dass dies ab sklearn 0.18 ist. In sklearn 0.17 kann dieselbe Funktion stattdessen in Modul cross_validation gefunden werden.

0
kadu

Eine kurze Pythonic-Lösung zum Ausgleich eines Pandas-DataFrames, entweder durch Unterabtastung (uspl=True) oder Überabtastung (uspl=False), die durch eine angegebene Spalte in diesem Datenrahmen mit zwei oder mehr Werten abgeglichen wird. 

Für uspl=True entnimmt dieser Code eine Zufallsstichprobe ohne Ersetzung mit einer Größe, die der kleinsten Schicht aller Schichten entspricht. Für uspl=False entnimmt dieser Code eine Zufallsstichprobe , wobei der Größe der größten Schicht aller Schichten entspricht. 

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) 

Dies funktioniert nur mit einem Pandas DataFrame, aber das scheint eine weit verbreitete Anwendung zu sein. Durch die Beschränkung auf Pandas DataFrames wird der Code, soweit ich das beurteilen kann, erheblich verkürzt. 

0
Roko Mijic