Scikit-learn 平衡子采样

69 投票
14 回答
91021 浏览
提问于 2025-04-18 05:18

我想从一个很大的不平衡数据集中创建N个平衡的随机子样本。请问有没有简单的方法可以用scikit-learn或pandas来实现,还是说我得自己动手写代码?有没有相关的代码可以参考一下?

这些子样本应该是随机的,并且可以有重叠,因为我会把每个子样本输入到一个非常大的分类器集合中的不同分类器里。

在Weka中有一个叫做spreadsubsample的工具,scikit-learn中有没有类似的功能? http://wiki.pentaho.com/display/DATAMINING/SpreadSubsample

(我知道有加权的方法,但这不是我想要的。)

14 个回答

6

这种数据拆分方式在 sklearn.cross_validation 提供的内置数据拆分技术中并不存在

看起来比较符合你需求的是 sklearn.cross_validation.StratifiedShuffleSplit,它可以生成任意大小的子样本,同时保持整个数据集的结构,也就是说,它会严格保持你主数据集中存在的那种不平衡状态。虽然这不是你想要的,但你可以参考里面的代码,把比例改成始终是50/50。

(如果你有兴趣,这可能会对scikit-learn做出很好的贡献。)

9

这是关于 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
12

我找到了一些很好的解决方案,可以在这里查看

而我觉得这个方法是最简单的。

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)

然后你可以使用X_rus, y_rus这些数据。

对于版本0.4及以上:

from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler()
X_rus, y_rus= rus.fit_sample(X, y)

然后,可以通过sample_indices_这个属性来获取随机选择的样本的索引。

39

现在有一个完整的Python工具包,可以帮助处理不平衡的数据。这个工具包可以在sklearn-contrib的项目中找到,地址是 https://github.com/scikit-learn-contrib/imbalanced-learn

30

这是我第一个版本,看起来运行得不错,欢迎大家复制或者提出建议,看看怎么能更高效一些(我在编程方面有不少经验,但在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数据框上,需要做几个修改:

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

撰写回答