我有一个带二进制类标签的数据集。我想从我的数据集中提取具有平衡类的样本。我在下面写的代码给了我不平衡的数据集。
sss = StratifiedShuffleSplit(train_size=5000, n_splits=1, test_size=50000, random_state=0)
for train_index, test_index in sss.split(X, y):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
print(itemfreq(y_train))
正如您所看到的,课程0
有2438个样本而课程1
有2562个。
[[ 0.00000000e+00 2.43800000e+03]
[ 1.00000000e+00 2.56200000e+03]]
我应如何在训练集中的每个课程1
和课程0
中获取2500个样本。 (并且测试设置也是25000)
答案 0 :(得分:3)
由于您没有向我们提供数据集,因此我使用通过make_blobs
生成的模拟数据。从您的问题中仍然不清楚应该有多少个测试样本。我已定义test_samples = 50000
,但您可以更改此值以满足您的需求。
train_samples = 5000
test_samples = 50000
total_samples = train_samples + train_samples
X, y = datasets.make_blobs(n_samples=total_samples, centers=2, random_state=0)
以下代码段将数据拆分为train并使用平衡类进行测试:
from sklearn.model_selection import StratifiedShuffleSplit
sss = StratifiedShuffleSplit(train_size=train_samples, n_splits=1,
test_size=test_samples, random_state=0)
for train_index, test_index in sss.split(X, y):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
演示:
In [54]: from scipy import stats
In [55]: stats.itemfreq(y_train)
Out[55]:
array([[ 0, 2500],
[ 1, 2500]], dtype=int64)
In [56]: stats.itemfreq(y_test)
Out[56]:
array([[ 0, 25000],
[ 1, 25000]], dtype=int64)
修改强>
正如@geompalik正确指出的那样,如果您的数据集不平衡StratifiedShuffleSplit
将不会产生平衡分割。在这种情况下,您可能会发现此功能很有用:
def stratified_split(y, train_ratio):
def split_class(y, label, train_ratio):
indices = np.flatnonzero(y == label)
n_train = int(indices.size*train_ratio)
train_index = indices[:n_train]
test_index = indices[n_train:]
return (train_index, test_index)
idx = [split_class(y, label, train_ratio) for label in np.unique(y)]
train_index = np.concatenate([train for train, _ in idx])
test_index = np.concatenate([test for _, test in idx])
return train_index, test_index
演示:
我已经预先生成了模拟数据,其中包含您指定的每个类的样本数量(此处未显示代码)。
In [153]: y
Out[153]: array([1, 0, 1, ..., 0, 0, 1])
In [154]: y.size
Out[154]: 55000
In [155]: train_ratio = float(train_samples)/(train_samples + test_samples)
In [156]: train_ratio
Out[156]: 0.09090909090909091
In [157]: train_index, test_index = stratified_split(y, train_ratio)
In [158]: y_train = y[train_index]
In [159]: y_test = y[test_index]
In [160]: y_train.size
Out[160]: 5000
In [161]: y_test.size
Out[161]: 50000
In [162]: stats.itemfreq(y_train)
Out[162]:
array([[ 0, 2438],
[ 1, 2562]], dtype=int64)
In [163]: stats.itemfreq(y_test)
Out[163]:
array([[ 0, 24380],
[ 1, 25620]], dtype=int64)
答案 1 :(得分:2)
问题在于,按定义使用的StratifiedShuffleSplit
方法通过按定义(分层)保留类的百分比来拆分。
在使用StratifiedShuffleSplit
时实现所需目标的直接方法是首先对主导类进行子采样,以便初始数据集平衡然后继续。使用numpy这很容易实现。虽然你描述的分裂几乎是平衡的。
答案 2 :(得分:0)
有很多方法可以达到平衡数据。
这是一种不需要sklearn的简单方法。
positives = []
negatives = []
for text, label in training_data:
if label == 1:
positives.append(text, label)
else:
negatives.append(text, label)
min_rows = min(len(positives), len(negatives))
# Finally, create a balanced data set using an equal number of positive and negative samples.
balanced_data = positives[0:min_rows]
balanced_data.extend(negatives[0:min_rows])
有关更高级的技术,请考虑签出imbalanced-learn。它是一个库,它在许多方面都与sklearn密切相关,但专门用于处理不平衡的数据。例如,它们提供了一堆代码来对数据进行欠采样或过采样。
答案 3 :(得分:0)
这是 pandas.DataFrame.sample 的包装器,它使用 weights
参数来执行平衡。它适用于 2 个以上的类和多个功能。
def pd_sample_balanced(X, y, n_times):
"""
Resample X and y with equal classes in y
"""
assert y.shape[0] == X.shape[0]
assert (y.index == X.index).all()
c = y.value_counts()
n_samples = c.max() * c.shape[0] * n_times
weights = (1 / (c / y.shape[0])).reset_index().rename(columns={"index": "y", 0: "w"})
weights = pd.DataFrame({"y": y}).merge(weights, on="y", how="left").w
X = X.sample(n=n_samples, weights=weights, random_state=random_state, replace=True)
y = y[X.index]
X = X.reset_index(drop=True)
y = y.reset_index(drop=True)
return X, y
示例用法
y1 = pd.Series([0, 0, 1, 1, 1, 1, 1, 1, 1, 2])
X1 = pd.DataFrame({"f1": np.arange(len(y1)), "f2": np.arange(len(y1))})
X2, y2 = pd_sample_balanced(X1, y1, 100)
print("before, y:")
print(y1.value_counts())
print("")
print("before, X:")
print(X1.value_counts())
print("")
print("after, y:")
print(y2.value_counts())
print("")
print("after, X:")
print(X2.value_counts())
示例输出
before, y:
1 7
0 2
2 1
dtype: int64
before, X:
f1 f2
9 9 1
8 8 1
7 7 1
6 6 1
5 5 1
4 4 1
3 3 1
2 2 1
1 1 1
0 0 1
dtype: int64
after, y:
2 720
0 691
1 689
Name: 0, dtype: int64
after, X:
f1 f2
9 9 720
1 1 361
0 0 330
7 7 110
6 6 104
4 4 98
3 3 98
8 8 97
5 5 94
2 2 88
dtype: int64