将均匀分布的值排序到预定义数量的组中的最简单方法是什么?
data = {'impact':[10,30,20,10,90,60,50,40]}
df = pd.DataFrame(data,index=['a','b','c','d','e','f','g','h'])
print df
impact
a 10
b 30
c 20
d 10
e 90
f 60
g 50
h 40
numgroups = 4
group_targetsum = round(df.impact.sum() / numgroups, -1)
print group_targetsum
80.0
在上面的例子中,我想从df创建4个组。唯一的排序标准是每组中的影响总和应大约等于group_targetsum。影响总和可以在合理范围内高于或低于group_targetsum。
最终,我想将这些群体分成他们自己的数据帧,保留索引。导致这样的事情:
print df_a
impact
e 90
print df_b
impact
c 20
f 60
print df_c
impact
a 10
d 10
g 50
print df_d
impact
b 30
h 40
结果数据框不需要完全相同,只要它们尽可能接近group_targetsum即可。
答案 0 :(得分:2)
假设系列中的值非常相似,这是使用searchsorted
-
In [150]: df
Out[150]:
impact
a 10
b 30
c 20
d 10
e 90
f 60
g 50
h 40
In [151]: a = df.values.ravel()
In [152]: shift_num = group_targetsum*np.arange(1,numgroups)
In [153]: idx = np.searchsorted(a.cumsum(), shift_num,'right')
In [154]: np.split(a, idx)
Out[154]: [array([10, 30, 20, 10]), array([90]), array([60]), array([50, 40])]
答案 1 :(得分:1)
从概念上讲,我们只想使用qcut
的加权版本,但目前在熊猫中并不存在。然而,我们可以通过组合cumsum
和cut
来完成同样的事情。 cumsum
基本上为我们提供了加权,然后我们用cut
对其进行切片。
(关于'csum_midpoint'的注意事项:如果没有中点调整,我们最终会根据它的开始位置(累积意义上)将事物分组,因此最终会偏向更高组中的分组。中点调整不能使事情变得完美,但它有所帮助。我相信这个答案在数学上与@Divakar相同,除了我在这里使用中点和使用'正确'。)
df['csum'] = df['impact'].cumsum()
df['csum_midpoint'] = (df.csum + df.csum.shift().fillna(0)) / 2.
df['grp'] = pd.cut( df.csum_midpoint, np.linspace(0,df['impact'].sum(),numgroups+1 ))
df.groupby( df.grp )['impact'].sum()
grp
(0, 77.5] 70
(77.5, 155] 90
(155, 232.5] 60
(232.5, 310] 90
Name: impact, dtype: int64
df
impact csum csum_midpoint grp
a 10 10 5.0 (0, 77.5]
b 30 40 25.0 (0, 77.5]
c 20 60 50.0 (0, 77.5]
d 10 70 65.0 (0, 77.5]
e 90 160 115.0 (77.5, 155]
f 60 220 190.0 (155, 232.5]
g 50 270 245.0 (232.5, 310]
h 40 310 290.0 (232.5, 310]