在 Python 中对稀疏数据集进行过采样

Posted

技术标签:

【中文标题】在 Python 中对稀疏数据集进行过采样【英文标题】:Oversampling a sparse dataset in Python 【发布时间】:2020-12-30 06:02:55 【问题描述】:

我有一个包含多标签数据的数据集。共有 20 个标签(从 0 到 20),它们之间的分布不平衡。以下是数据概览:

|id   |label|value       |
|-----|-----|------------|
|95534|0    |65.250002088|
|95535|18   |            |
|95536|0    |            |
|95536|0    |100         |
|95536|0    |            |
|95536|0    |53.68547236 |
|95536|0    |            |
|95537|1    |            |
|95538|0    |            |
|95538|0    |            |
|95538|0    |            |
|95538|0    |656.06155202|
|95538|0    |            |
|95539|2    |            |
|5935 |0    |            |
|5935 |0    |150         |
|5935 |0    |50          |
|5935 |0    |24.610985335|
|5935 |0    |            |
|5935 |0    |223.81789584|
|5935 |0    |148.1805218 |
|5935 |0    |110.9712538 |
|34147|19   |73.62651909 |
|34147|19   |            |
|34147|19   |53.35958016 |
|34147|19   |            |
|34147|19   |            |
|34147|19   |            |
|34147|19   |393.54029411|

我希望对数据进行过采样并在标签之间取得平衡。我遇到了一些方法,例如SMOTESMOTENC,但它们都需要将数据拆分为训练集和测试集,并且它们不适用于稀疏数据。有什么方法可以在拆分前的预处理步骤中对整个数据执行此操作?

【问题讨论】:

【参考方案1】:

对行进行采样,以使每个label 以相等的概率被采样:

绘制一行给定标签的概率应该是1/n_labels 为给定标签 l 绘制给定行的概率应该是 1/n_rows 对于该标签中的 n_rows

那么每一行的概率为p_row = 1/(n_labels*n_rows)。您可以使用 groupby 生成这些值并将它们传递给 df.sample,如下所示:

import numpy as np
import pandas as pd

df_dict = 'id': 0: 95535, 1: 95536, 2: 95536, 3: 95536, 4: 95536, 5: 95536, 6: 95537, 7: 95538, 8: 95538, 9: 95538, 10: 95538, 11: 95538, 12: 95539, 13: 5935, 14: 5935, 15: 5935, 16: 5935, 17: 5935, 18: 5935, 19: 5935, 20: 5935, 21: 34147, 22: 34147, 23: 34147, 24: 34147, 25: 34147, 26: 34147, 27: 34147, 'label': 0: 18, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 1, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 2, 13: 0, 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0, 21: 19, 22: 19, 23: 19, 24: 19, 25: 19, 26: 19, 27: 19, 'value': 0: '            ', 1: '            ', 2: '100         ', 3: '            ', 4: '53.68547236 ', 5: '            ', 6: '            ', 7: '            ', 8: '            ', 9: '            ', 10: '656.06155202', 11: '            ', 12: '            ', 13: '            ', 14: '150         ', 15: '50          ', 16: '24.610985335', 17: '            ', 18: '223.81789584', 19: '148.1805218 ', 20: '110.9712538 ', 21: '73.62651909 ', 22: '            ', 23: '53.35958016 ', 24: '            ', 25: '            ', 26: '            ', 27: '393.54029411'    

df = pd.DataFrame.from_dict(d)

# create column that includes counts by label
n_labels = df.label.nunique()
n_rows = df.groupby("label").id.transform("count")
weights = 1/(n_rows*n_labels)

# sanity check probabilities:
bool(np.sum(weights) == 1)    

df_samples = df.sample(n=40000, weights=weights, replace=True, random_state=19)

验证标签绘制是否大致一致:

print(df_samples.label.value_counts()/len(df_samples))

# sampling frequency by group:
# 0     0.203325
# 2     0.201075
# 18    0.200925
# 19    0.198850
# 1     0.195825

【讨论】:

【参考方案2】:

实际上从理论上讲,您不需要对测试集进行上采样。

在类不平衡设置中,人为地平衡测试/验证集没有任何意义:这些集必须保持真实,即您想在现实世界设置中测试分类器性能,例如,负类将包括99% 的样本,以了解您的模型在预测 1% 的感兴趣的正类类别方面的表现如何,而没有太多的误报。人为地夸大少数类或减少多数类会导致性能指标不切实际,与您要解决的现实世界问题没有真正的关系。

重新平衡仅在训练集中才有意义,以防止分类器简单天真地将所有实例分类为负值,从而获得 99% 的感知准确度。

因此,您可以放心,在您描述的设置中,重新平衡仅对训练集/折叠采取行动。

【讨论】:

感谢您的回答。你是对的,我同意你的观点,但我的情况是采样大量数据,但我无法捕获样本中可能的标签。这就是我考虑过采样的原因。

以上是关于在 Python 中对稀疏数据集进行过采样的主要内容,如果未能解决你的问题,请参考以下文章

Tensorflow 数据集 API 中的过采样功能

如何对不平衡的多类数据集进行欠采样? (Python)

稀疏数据集上的光谱聚类

我们可以在 Python 中对多元时间序列数据集进行聚类吗

点云的滤波

如何在 Pyspark 中对数据框进行过采样?