J'essaie de créer N sous-échantillons aléatoires équilibrés de mon grand ensemble de données non équilibré. Existe-t-il un moyen de faire cela simplement avec scikit-learn/pandas ou dois-je le mettre en œuvre moi-même? Des pointeurs sur le code qui fait cela?
Ces sous-échantillons doivent être aléatoires et se chevaucher car je charge chacun d’entre eux de séparer un classificateur dans un très grand ensemble de classificateurs.
Dans Weka, il existe un outil appelé spreadsubsample, existe-t-il un équivalent dans sklearn? http://wiki.pentaho.com/display/DATAMINING/SpreadSubsample
(Je connais la pondération, mais ce n'est pas ce que je recherche.)
Il existe maintenant un package python complet pour traiter les données déséquilibrées. Il est disponible sous forme de package sklearn-contrib à https://github.com/scikit-learn-contrib/imbalanced-learn
Une version pour 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
Ce type de fractionnement de données est non fourni par les techniques de fractionnement de données intégrées exposées dans sklearn.cross_validation
.
sklearn.cross_validation.StratifiedShuffleSplit
semble similaire à vos besoins. Il peut générer des sous-échantillons de toute taille tout en conservant la structure de l'ensemble de données, c'est-à-dire méticuleusement en appliquant le même déséquilibre que celui de votre ensemble de données principal. Bien que ce ne soit pas ce que vous recherchez, vous pourrez peut-être utiliser le code et modifier le ratio imposé à 50/50 toujours.
(Ce serait probablement une très bonne contribution à scikit-learn si vous vous sentez à la hauteur.)
Voici une version du code ci-dessus qui fonctionne pour les groupes multiclass (dans mon groupe de cas testé 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)
Cela renvoie également les index pour qu'ils puissent être utilisés avec d'autres jeux de données et pour garder une trace de la fréquence d'utilisation de chaque jeu de données (utile pour la formation).
Ci-dessous, mon implémentation en python pour créer une copie de données équilibrée. Hypothèses: 1. la variable cible (y) est une classe binaire (0 vs. 1) 2. 1 est la minorité.
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]
Une légère modification de la réponse de mikkom.
Si vous souhaitez conserver l'ordre des données de classe plus importantes, par exemple. vous ne voulez pas mélanger.
Au lieu de
if len(this_xs) > use_elems:
np.random.shuffle(this_xs)
fais ça
if len(this_xs) > use_elems:
ratio = len(this_xs) / use_elems
this_xs = this_xs[::ratio]
Sélectionnez simplement 100 lignes dans chaque classe avec des doublons en utilisant le code suivant. activity
est mes classes (étiquettes de l'ensemble de données)
balanced_df=Pdf_train.groupby('activity',as_index = False,group_keys=False).apply(lambda s: s.sample(100,replace=True))
Ma version de sous-échantillonnage, espérons que cela aide
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]]
Bien qu'il y ait déjà une réponse, je suis tombé sur votre question en cherchant quelque chose de similaire. Après quelques recherches supplémentaires, je crois que sklearn.model_selection.StratifiedKFold
peut être utilisé à cette fin:
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])
Il est important que vous ajoutiez le _
car, puisque skf.split()
est utilisé pour créer des plis stratifiés pour la validation croisée des plis K, il renvoie deux listes d'index: train
(n - 1 / n
éléments) et test (1 / n
éléments).
Veuillez noter qu’il s’agit du sklearn 0,18 . Dans sklearn 0.17 la même fonction peut être trouvée dans le module cross_validation
à la place.
Une solution courte et Pythonic pour équilibrer un DataFrame de pandas par sous-échantillonnage (uspl=True
) ou suréchantillonnage (uspl=False
), équilibrée par une colonne spécifiée dans cette trame de données ayant deux valeurs ou plus.
Pour uspl=True
, ce code prendra un échantillon aléatoire sans remplacement de taille égale à la plus petite strate de toutes les strates. Pour uspl=False
, ce code prendra un échantillon aléatoire avec remplacement de taille égale à la plus grande strate de toutes les strates.
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)
Cela ne fonctionnera qu'avec un Pandas DataFrame, mais cela semble être une application courante, et le limiter à Pandas DataFrames raccourcit considérablement le code, à ma connaissance.