votingclassifier sklearn forest python pandas scikit-learn subsampling

python - forest - sklearn adaboost



Scikit-learn subsampling equilibrado (10)

Estoy intentando crear N subgrupos aleatorios equilibrados de mi gran conjunto de datos desequilibrado. ¿Hay alguna forma de hacerlo simplemente con scikit-learn / pandas o tengo que implementarlo yo mismo? ¿Algún puntero a código que haga esto?

Estas submuestras deben ser aleatorias y pueden superponerse a medida que alimente cada una de ellas para separar el clasificador en un conjunto muy grande de clasificadores.

En Weka hay una herramienta llamada spreadsubsample, ¿hay equivalente en sklearn? http://wiki.pentaho.com/display/DATAMINING/SpreadSubsample

(Sé sobre el peso, pero eso no es lo que estoy buscando).


A continuación se muestra mi implementación de Python para crear una copia de datos equilibrada. Supuestos: 1. la variable objetivo (y) es una clase binaria (0 vs. 1) 2. 1 es la minoría.

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]



Aquí está mi primera versión que parece estar funcionando bien, siéntase libre de copiar o hacer sugerencias sobre cómo podría ser más eficiente (tengo una experiencia bastante larga con la programación en general, pero no tanto con python o numpy)

Esta función crea una única submuestra aleatoria equilibrada.

edición: el tamaño de la submuestra ahora muestra muestras de clases minoritarias, esto probablemente debería cambiarse.

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

Para cualquiera que intente hacer que el trabajo anterior funcione con un DataFrame de Pandas, necesita hacer un par de cambios:

  1. Reemplace la línea np.random.shuffle con

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

  2. Reemplace las líneas np.concatenate con

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


Aquí hay una versión del código anterior que funciona para grupos multiclase (en mi grupo de casos comprobados 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)

Esto también devuelve los índices para que se puedan usar para otros conjuntos de datos y para realizar un seguimiento de la frecuencia con la que se usó cada conjunto de datos (útil para la capacitación)


Aunque ya está respondida, me topé con tu pregunta buscando algo similar. Después de un poco más de investigación, creo que sklearn.model_selection.StratifiedKFold se puede usar para este propósito:

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 importante que agregue _ porque dado que skf.split() se usa para crear pliegues estratificados para la validación cruzada K-fold, devuelve dos listas de índices: train ( n - 1 / n elementos) y prueba ( 1 / n elementos).

Tenga en cuenta que esto es a partir de 0.18 sklearn . En sklearn 0.17, la misma función se puede encontrar en el módulo cross_validation .


Este tipo de división de datos no se proporciona entre las técnicas de división de datos incorporadas expuestas en sklearn.cross_validation .

Lo que parece similar a sus necesidades es sklearn.cross_validation.StratifiedShuffleSplit , que puede generar submuestras de cualquier tamaño al tiempo que conserva la estructura de todo el conjunto de datos, es decir, aplica meticulosamente el mismo desequilibrio que se encuentra en su conjunto de datos principal. Si bien esto no es lo que está buscando, es posible que pueda usar el código y cambiar la proporción impuesta a 50/50 siempre.

(Esto probablemente sería una muy buena contribución a scikit-learn si te sientes capaz de hacerlo).


Mi versión subsampler espero que esto ayude

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


Una ligera modificación a la respuesta superior por parte de mikkom.

Si desea conservar el orden de los datos de clase más grandes, es decir. no quieres barajar

En lugar de

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

hacer esto

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


Una solución corta y pythonic para equilibrar un marco de datos de pandas mediante un submuestreo ( uspl=True ) o un uspl=False ( uspl=False ), balanceado por una columna específica en ese marco de datos que tiene dos o más valores.

Para uspl=True , este código tomará una muestra aleatoria sin reemplazo de tamaño igual al estrato más pequeño de todos los estratos. Para uspl=False , este código tomará una muestra aleatoria con reemplazo de tamaño igual al estrato más grande de todos los estratos.

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)

Esto solo funcionará con Pandas DataFrame, pero esa parece ser una aplicación común, y restringirla a Pandas DataFrames acorta significativamente el código hasta donde puedo decir.


Una versión para 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