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