【问题标题】:Groupby and random sample based on a column value基于列值的分组和随机样本
【发布时间】:2020-09-19 17:34:28
【问题描述】:

我有一个数据框,我想按grp1, grp2 列分组,然后根据how_many 列从每个组中随机抽样。

这是我的示例数据

   grp1  grp2  how_many    val
0     a     1         2  2993
1     a     1         2  8244
2     a     2         1  7148
3     a     1         2  5326
4     a     3         2  5577
5     a     3         2  5651
6     a     1         2  6297
7     a     2         1  2657
8     a     2         1  9774
9     a     1         2  4075
10    a     3         2  6780
11    b     1         1  1765
12    b     1         1  5592
13    b     1         1  9936
14    b     2         4  4324
15    b     2         4  6823
16    b     2         4  9184
17    b     2         4  7498
18    b     2         4  3810

这是预期的结果(当然是随机的):

  grp1  grp2  how_many    val
0    a     1         2  2993
1    a     1         2  5326
2    a     2         1  9774
3    a     3         2  6780
4    a     3         2  5651
5    b     1         1  5592
6    b     2         4  6823
7    b     2         4  9184
8    b     2         4  7498
9    b     2         4  3810

我的方法是遵循these instructions,但是,就我而言,我没有稳定的样本量,它会根据列值而变化。

我还尝试在 groupby 列上使用 multi_index,但收到错误消息,提示 MemoryError: Unable to allocate 107. GiB for an array with shape (57244869081,) and data type int16。这只是我数据的一小部分。

任何帮助将不胜感激

【问题讨论】:

  • 不,不是。那里的样本量是标准的,准确地说是 8000。就我而言,它基于列值

标签: python pandas pandas-groupby


【解决方案1】:

如果每组 how_many 列中的值相同,则两种解决方案都有效。

使用DataFrame.sample 创建 lambda 函数并按组调用:

df1= (df.groupby(['grp1','grp2'])
        .apply(lambda x: x.sample(x['how_many'].iat[0]))
        .reset_index(drop=True))
print (df1)
  grp1  grp2  how_many   val
0    a     1         2  2993
1    a     1         2  6297
2    a     2         1  9774
3    a     3         2  5651
4    a     3         2  5577
5    b     1         1  1765
6    b     2         4  6823
7    b     2         4  4324
8    b     2         4  7498
9    b     2         4  9184

提高性能的开箱即用解决方案:

首先通过DataFrame.sample中的frac=1创建随机所有行,然后通过GroupBy.cumcount创建计数器并通过Series.le比较<=,最后如果需要按组排序:

df1 = df.sample(frac=1)
df1 = df1[df1.groupby(['grp1','grp2']).cumcount().add(1).le(df1['how_many'])]
df1 = df1.sort_values(['grp1','grp2'])
print (df1)
   grp1  grp2  how_many   val
0     a     1         2  2993
1     a     1         2  8244
2     a     2         1  7148
5     a     3         2  5651
4     a     3         2  5577
13    b     1         1  9936
16    b     2         4  9184
17    b     2         4  7498
15    b     2         4  6823
14    b     2         4  4324

详情

首先是随机改变的列顺序:

df1 = df.sample(frac=1)
print (df1)
   grp1  grp2  how_many   val
15    b     2         4  6823
18    b     2         4  3810
10    a     3         2  6780
11    b     1         1  1765
14    b     2         4  4324
2     a     2         1  7148
16    b     2         4  9184
7     a     2         1  2657
9     a     1         2  4075
1     a     1         2  8244
13    b     1         1  9936
17    b     2         4  7498
8     a     2         1  9774
4     a     3         2  5577
12    b     1         1  5592
0     a     1         2  2993
5     a     3         2  5651
6     a     1         2  6297
3     a     1         2  5326

然后为每个组创建计数器并按掩码过滤 - 按列比较 how_many 返回第一行,但因为随机更改的顺序返回随机行:

print (df1.assign(counter=df1.groupby(['grp1','grp2']).cumcount().add(1), 
                  mask=df1.groupby(['grp1','grp2']).cumcount().add(1).le(df1['how_many'])))
   grp1  grp2  how_many   val  counter   mask
15    b     2         4  6823        1   True
18    b     2         4  3810        2   True
10    a     3         2  6780        1   True
11    b     1         1  1765        1   True
14    b     2         4  4324        3   True
2     a     2         1  7148        1   True
16    b     2         4  9184        4   True
7     a     2         1  2657        2  False
9     a     1         2  4075        1   True
1     a     1         2  8244        2   True
13    b     1         1  9936        2  False
17    b     2         4  7498        5  False
8     a     2         1  9774        3  False
4     a     3         2  5577        2   True
12    b     1         1  5592        3  False
0     a     1         2  2993        3  False
5     a     3         2  5651        3  False
6     a     1         2  6297        4  False
3     a     1         2  5326        5  False

过滤:

df1 = df1[df1.groupby(['grp1','grp2']).cumcount().add(1).le(df1['how_many'])]
print (df1)
   grp1  grp2  how_many   val
15    b     2         4  6823
18    b     2         4  3810
10    a     3         2  6780
11    b     1         1  1765
14    b     2         4  4324
2     a     2         1  7148
16    b     2         4  9184
9     a     1         2  4075
1     a     1         2  8244
4     a     3         2  5577

如有必要,排序:

df1 = df1.sort_values(['grp1','grp2'])
print (df1)
   grp1  grp2  how_many   val
9     a     1         2  4075
1     a     1         2  8244
2     a     2         1  7148
10    a     3         2  6780
4     a     3         2  5577
11    b     1         1  1765
15    b     2         4  6823
18    b     2         4  3810
14    b     2         4  4324
16    b     2         4  9184

【讨论】:

  • 不错的 lambda 和示例!
  • 感谢您的回答,您能否解释一下内联 cmets 的第二种解决方案?它似乎是一个矢量化解决方案,应该优于第一个解决方案。因为我打算进行大约 1k 次随机抽样以获得结果的置信区间。
猜你喜欢
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 2017-04-15
  • 1970-01-01
  • 2018-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多