从pandas数据框中抽样数据

3

我正在尝试从一个大数据集中抽样。

这个数据集长这样

id      label
1       A
2       B
3       C
4       A
.........

生成样本数据集的代码

labels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N']
df = pd.DataFrame()
N = 300000
weights = [0.350019, 0.209966, 0.126553, 0.100983, 0.053767, 0.039378, 0.029529,
           0.019056, 0.016783, 0.014813, 0.014152, 0.013477, 0.009444, 0.002082]
import random
df['id'] = list(range(1, N+1))
df['label'] = list(random.choices(labels, weights=weights, k=N))

group_dict= df.groupby(['id']).apply(lambda x: list(set(x['label'].tolist()))[0]).to_dict()
df = pd.DataFrame(group_dict.items())
df.columns= ['id','label']

数据集中标签的分布情况为:

df['label'].value_counts(normalize=True)

A    0.350373
B    0.209707
C    0.126307
D    0.101353
E    0.053917
F    0.039487
G    0.029217
H    0.018780
I    0.016510
J    0.015083
K    0.014323
L    0.013467
M    0.009530
N    0.001947

我在数据集中创建了一个新列。
df['freq'] = df.groupby('label')['label'].transform('count')

当我试图取样,例如5000个项目时

sampledf = df.sample(n=5000, weights=df.freq,
                          random_state=42)

sampledf中标签的分布与df中的不同

A    0.6048
B    0.2198
C    0.0850
D    0.0544
E    0.0190
F    0.0082
G    0.0038
H    0.0020
I    0.0010
K    0.0008
L    0.0008
J    0.0004

我不确定为什么分布与实际数据框不同。 有人能帮我看看我漏了什么吗? 谢谢。

你能发布一个MRE吗?你已经接近成功了,但我们实际上不知道df是如何定义的,例如。 - Michael Delgado
@MichaelDelgado 我已经添加了代码来生成模拟我面临的问题的数据集。 - Ashwani K
看起来很好。我的回答仍然适用 - 在从df中进行抽样时,您需要停止使用权重。如果您只是调用df.sample,那么就没问题了。 - Michael Delgado
1个回答

3
如果你正在将频率重新分配到原始数据框中,那可能是问题所在。确保你的抽样中没有重复的标签 权重。
使用您的摘要数据,我可以生成大约相同分布的5000个样本:
In [1]: import pandas as pd

In [2]: summary = pd.DataFrame(
   ...:    [
   ...:        ['A', 0.350019],
   ...:        ['B', 0.209966],
   ...:        ['C', 0.126553],
   ...:        ['D', 0.100983],
   ...:        ['E', 0.053767],
   ...:        ['F', 0.039378],
   ...:        ['G', 0.029529],
   ...:        ['H', 0.019056],
   ...:        ['I', 0.016783],
   ...:        ['J', 0.014813],
   ...:        ['K', 0.014152],
   ...:        ['L', 0.013477],
   ...:        ['M', 0.009444],
   ...:        ['N', 0.002082],
   ...:    ],
   ...:    columns=['label', 'freq']
   ...: )

您可以从汇总表中进行采样,将每个 唯一的 标签根据其在原数据集中的频率赋予权重:
In [3]: summary.label.sample(
   ...:     n=5000,
   ...:     weights=summary.freq,
   ...:     replace=True,
   ...: ).value_counts(normalize=True)

Out[3]:
label
A    0.3448
B    0.2198
C    0.1356
D    0.0952
E    0.0488
F    0.0322
G    0.0284
H    0.0234
I    0.0168
J    0.0162
K    0.0146
L    0.0140
M    0.0090
N    0.0012
dtype: float64

或者,你可以完全跳过频率的计算 - pandas 会为你做这个:

In [7]: df = pd.DataFrame(np.random.choice(["A", "B", "C", "D"], size=20_000, p=[0.6, 0.3, 0.05, 0.05]), columns=["label"])
In [8]: df.label.sample(5000, replace=True).value_counts(normalize=True)
Out[8]:
A    0.5994
B    0.2930
C    0.0576
D    0.0500
Name: label, dtype: float64

你问题中的代码存在问题,因为你最终权重是基于词频和显式权重(也考虑了频率)的加权:
In [2]: df = pd.DataFrame(np.random.choice(["A", "B", "C", "D"], size=20_000, p=[0.6, 0.3, 0.05, 0.05]), columns=["label"])
In [3]: df['frequency'] = df.groupby('label')['label'].transform('count')
In [4]: df
Out[4]:
        label   frequency
    0   A       11908
    1   A       11908
    2   B       5994
    3   B       5994
    4   D       1033
  ...   ...     ...
19995   A       11908
19996   D       1033
19997   A       11908
19998   A       11908
19999   A       11908

结果大致等于每个频率的规范化平方。
In [6]: freqs = np.array([0.6, 0.3, 0.05, 0.05])
In [7]: (freqs ** 2) / (freqs ** 2).sum()
Out[7]:
array([0.79120879, 0.1978022 , 0.00549451, 0.00549451])

1
我猜 .to_frame().groupby('label').size() / 5000 相当于 .value_counts(normalize=True) - Mustafa Aydın
另外,如果您考虑这个MRE:df = pd.DataFrame(np.random.choice(["A", "B", "C", "D"], size=20_000, p=[0.6, 0.3, 0.05, 0.05]), columns=["label"]),并且应用您的方法(除了我传递未归一化的权重给.sample,Pandas应该会进行归一化),我得到的结果是:A 0.7984 B 0.1904 D 0.0062 C 0.0050,这与生成数据的分布不匹配... - Mustafa Aydın
但是再次强调 - 这是因为您传递了权重和重复值。Pandas使用重复的权重从重复的标签中进行抽样。因此,A会出现多次,并且每个A都具有更高的权重。要么使用权重进行抽样,要么从完整的数据框中进行抽样,不要同时使用两者。 - Michael Delgado
是的,sampledf = df.sample(n=5000, random_state=42) 就解决了问题。谢谢。 - Ashwani K
感谢解释为什么发生了分布变化。我希望 Pandas 在他们的文档中加入以下说明:Pandas 正在使用重复权重从重复标签中进行采样...请选择使用权重进行采样或者对整个数据集进行采样,不要同时使用两种方法。 - Iopheam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接