Pandas Groupby with Lambda and Algorithm

时间:2017-05-30 17:00:38

标签: python pandas lambda group-by

鉴于此数据框:

import pandas as pd
import jenkspy
f = pd.DataFrame({'BreakGroup':['A','A','A','A','A','A','B','B','B','B','B'],
                 'Final':[1,2,3,4,5,6,10,20,30,40,50]})
    BreakGroup  Final
0         A     1
1         A     2
2         A     3
3         A     4
4         A     5
5         A     6
6         B     10
7         B     20
8         B     30
9         B     40
10        B     50

我想根据4组(班级)的自然休息时间使用jenkspy来识别该组,其中每个值都在" Final"在小组内#34; BreakGroup"所属。

我开始这样做:

jenks=lambda x: jenkspy.jenks_breaks(f['Final'].tolist(),nb_class=4)
f['Group']=f.groupby(['BreakGroup'])['BreakGroup'].transform(jenks)

...导致:

BreakGroup
A    [1.0, 10.0, 20.0, 30.0, 50.0]
B    [1.0, 10.0, 20.0, 30.0, 50.0]
Name: BreakGroup, dtype: object

这里的第一个问题,正如你可能已经推测的那样,它将lambda函数应用于" Final"的整个列。分数而不仅仅是属于Groupby中每个组的分数。第二个问题是我需要一个列指定正确的组(类)成员资格,可能是通过使用transform而不是apply。

然后我尝试了这个:

jenks=lambda x: jenkspy.jenks_breaks(f['Final'].loc[f['BreakGroup']==x].tolist(),nb_class=4)
f['Group']=f.groupby(['BreakGroup'])['BreakGroup'].transform(jenks)

......但是很快又被提交回来了:

ValueError: Can only compare identically-labeled Series objects

更新

这是期望的结果。 "结果"列包含来自"最终"的相应值的组的上限。每组" BreakGroup":

    BreakGroup  Final   Result
0             A     1   2
1             A     2   3
2             A     3   4
3             A     4   4
4             A     5   6
5             A     6   6
6             B     10  20
7             B     20  30
8             B     30  40
9             B     40  50
10            B     50  50

提前致谢!

我根据公认的解决方案略微修改了应用程序:

f.sort_values('BreakGroup',inplace=True)
f.reset_index(drop=True,inplace=True)
jenks = lambda x: jenkspy.jenks_breaks(x['Final'].tolist(),nb_class=4)
g = f.set_index('BreakGroup')
g['Groups'] = f.groupby(['BreakGroup']).apply(jenks)
g.reset_index(inplace=True)
groups= lambda x: [gp for gp in x['Groups']]
#'final' value should be > lower and <= upper
upper = lambda x: [gp for gp in x['Groups'] if gp >= x['Final']][0] # or gp == max(x['Groups'])
lower= lambda x: [gp for gp in x['Groups'] if gp < x['Final'] or gp == min(x['Groups'])][-1]
GroupIndex= lambda x: [x['Groups'].index(gp) for gp in x['Groups'] if gp < x['Final'] or gp == min(x['Groups'])][-1]
f['Groups']=g.apply(groups, axis=1)
f['Upper'] = g.apply(upper, axis=1)
f['Lower'] = g.apply(lower, axis=1)
f['Group'] = g.apply(GroupIndex, axis=1)
f['Group']=f['Group']+1

返回:

  1. 组边界列表

  2. 上限与&#34;最终&#34;

  3. 的值相关
  4. 与&#34;最终&#34;

  5. 的值相关的下边界
  6. &#34; Final&#34;的值。将基于评论中提到的逻辑属于。

2 个答案:

答案 0 :(得分:3)

您已将jenks定义为x的常量,即lambda变量,因此它不依赖于您使用apply或{{1 }}。将transform的定义更改为

jenks

给出

jenks = lambda x: jenkspy.jenks_breaks(x['Final'].tolist(),nb_class=4)

继续这种重新定义,

In [315]: f.groupby(['BreakGroup']).apply(jenks)
Out[315]: 
BreakGroup
A         [1.0, 2.0, 3.0, 4.0, 6.0]
B    [10.0, 20.0, 30.0, 40.0, 50.0]
dtype: object

给出

g = f.set_index('BreakGroup')
g['Groups'] = f.groupby(['BreakGroup']).apply(jenks)
g.reset_index(inplace=True)
group = lambda x: [gp for gp in x['Groups'] if gp > x['Final'] or gp == max(x['Groups'])][0]
f['Result'] = g.apply(group, axis=1)

答案 1 :(得分:1)

目前,您正在将一系列传递到transform()而不是标量,因为您打算使用过滤条件。考虑为x.index[0]等第一个值建立索引,因为groupby系列中的所有值都相同。您甚至可以投放min(x)max(x)

lambda x: jenkspy.jenks_breaks(f['Final'].loc[f['BreakGroup']==x.index[0]].tolist(), nb_class=4)

f['Group'] = f.groupby(['BreakGroup'])['BreakGroup'].transform(jenks)