Scikit-learn сбалансированная подвыборка

Я пытаюсь создать N сбалансированных случайных подвыборки моего большого несбалансированного набора данных. Есть ли способ сделать это просто с помощью scikit-learn / pandas или я должен сам его реализовать? Любые указатели на код, который это делает?

Эти подвыборки должны быть случайными и могут перекрываться, когда я подаю каждый отдельный классификатор в очень большой ансамбль классификаторов.

В Weka есть инструмент под названием spreadsubsample, есть ли эквивалент в sklearn? http://wiki.pentaho.com/display/DATAMINING/SpreadSubsample

(Я знаю о взвешивании, но это не то, что я ищу).

9 Solutions collect form web for “Scikit-learn сбалансированная подвыборка”

Вот моя первая версия, которая, кажется, работает нормально, не стесняйтесь копировать или делать предложения о том, как она может быть более эффективной (у меня довольно много опыта программирования в целом, но не так долго с 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')

Версия для pandas Серия :

 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 

Этот тип разделения данных не предоставляется среди встроенных методов разделения данных, открытых в sklearn.cross_validation .

То, что похоже на ваши потребности, это sklearn.cross_validation.StratifiedShuffleSplit , который может генерировать подвыборки любого размера, сохраняя при этом структуру всего набора данных, т. Е. Тщательно применяя тот же дисбаланс, который находится в вашем основном наборе данных. В то время как это не то, что вы ищете, вы можете использовать код в нем и постоянно менять установленное соотношение до 50/50.

(Это, вероятно, будет очень хорошим вкладом в scikit-learn, если вы к этому позаботитесь.)

Вот версия вышеуказанного кода, которая работает для многоклассовых групп (в моей проверенной группе случаев 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) 

Это также возвращает индексы, чтобы они могли использоваться для других наборов данных и отслеживать, как часто использовался каждый набор данных (полезно для обучения)

Небольшая модификация верхнего ответа от микком.

Если вы хотите сохранить порядок больших данных класса, т.е. вы не хотите перетасовывать.

Вместо

  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] 

Моя подсимплексорная версия, надеюсь, это поможет

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

Ниже приведена моя реализация 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] 

Хотя на это уже ответил, я наткнулся на ваш вопрос, ища что-то подобное. После нескольких исследований я считаю, что для этой цели можно использовать 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-fold, он возвращает два списка индексов: train ( n - 1 / n элементов) и test ( 1 / n элементы).

Обратите внимание, что это относится к sklearn 0.18 . В sklearn 0.17 такая же функция может быть найдена в модуле cross_validation .

Краткое, pythonic решение для баланса pandas DataFrame либо путем подвыборки ( uspl=True ), либо передискретизацией ( uspl=False ), уравновешенной определенным столбцом в этом фрейме данных, который имеет два или более значений.

Для 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 значительно сокращает код, насколько я могу судить.

Python - лучший язык программирования в мире.