如何按多列的函数分组Pandas行

4

我有一个数据框,其中记录了建筑物屋顶表面的特征,每个建筑物都有多个平面,具有面积和形式描述。例如:

df=pd.DataFrame([[1000, 12, 'slope'],
                [1000, 10, 'flat'],
                [1001, 10, 'slope'],
                [1001, 15, 'flat'],
                [1001, 7, 'slope']],
               index = [1,2,3,4,5],
               columns=['building_id', 'area', 'form'],
               )
df
building_id     area    form
1   1000    12  slope
2   1000    10  flat
3   1001    10  slope
4   1001    15  flat
5   1001    7   slope

我希望将行合并,每座建筑物仅有一行,包括总屋顶面积和占该建筑物最大面积的主要屋顶形式 - 即不是最常见的形式,而是最大面积的形式:
df_out
building_id     area    form
    1   1000    22  slope
    2   1001    32  slope

我需要类似这样的东西:

group_functions={'area' : ['sum'],
                 'form' : lambda x: find_predominant(x)}
df_out = df.groupby('building_id').agg(group_functions)

但是find_predominant需要同时考虑areaform的函数:它返回字符串'flat''slope',具体取决于哪一个对于该building_id有最大的面积。

find_predominant函数是什么?或者有哪个脚本能够达到相同的效果?


你的结果无法重现,因为你没有包含 find_predominant - cs95
我不知道find_predominant是什么 - 这就是问题所在!会进行编辑以增加清晰度。 - doctorer
好的,我编辑了我的回答。下次请让这更清晰些。 - cs95
2个回答

3

我的建议是先计算总和,然后单独调用 find_predominant 函数,因为这将需要调用 apply

g = df.groupby('building_id')
area = g['area'].sum()
form = g.apply(find_predominant) 

df_out = pd.concat([area, form], axis=1)

现在,为了使这个工作正常运行,请注意find_predominant应该接受一个DataFrame并适当访问"area"和"form"列。

def find_predominant(df):
    ar = df['area']
    fm = df['form']
    ... # Do something with ar and fm

    return result

这可能需要您进行重构,也可能不需要。


编辑:好的,那么您不知道这个函数是什么。在这种情况下,让我们将其删除。

请尝试这个。

area = df.groupby('building_id')['area'].sum()
form = (df.groupby(['building_id', 'form'])['area']
          .sum()
          .groupby(level=0)
          .idxmax()
          .str[1])
form.name = 'form'

df_out = pd.concat([area, form], axis=1).reset_index()
print(df_out)
   building_id  area   form
0         1000    22  slope
1         1001    32  slope

这将选择与每个building_id持有的最大面积(按总和计算)相对应的表单。

如果不需要通过最大总和来获取表单,而只需要通过最大面积来获取表单,那么解决方案就更简单了。

g = df.groupby('building_id')['area']
area = g.sum()
form = (df.set_index('building_id')
          .iloc[g.idxmax(), df.columns.get_loc('form') - 1])

df_out = pd.concat([area, form], axis=1).reset_index()
print(df_out)
   building_id  area   form
0         1000    22   flat
1         1001    32  slope

在您的第一次编辑中,应该是 area = df.groupby('building_id').area.sum() 吗? - doctorer
@doctorer 正确,谢谢。然而,在这种情况下,由于面积是唯一不在分组器中的数字列,因此它将是“sum”输出中唯一的列。 - cs95
你说得对。我正在将它应用于具有多列的真实数据框,因此你的编辑更清晰。 - doctorer

2
您可以使用sort_values并在agg之后赋值。
(df.groupby(['building_id','form'])['area']
   .sum()
   .sort_values()
   .reset_index(level=1)
   .groupby(level=0)
   .agg({'form':'last','area':'sum'}))

              form  area
building_id             
1000         slope    22
1001         slope    32

1
我认为idxmax在这里不起作用。主要标签必须基于总和进行分配。 - cs95
@coldspeed 现在不知道....它会返回字符串“flat”或“slope”,具体取决于该建筑物ID的哪个区域面积更大。 - BENY
很不幸,它能够工作是因为这个数据并没有明确表明它不会在每个实例中都起作用。将最后一个子列表替换为[1001,6,'slope'],然后再次运行代码。它应该给出“斜坡”(因为16(斜率)> 15(平坦)),但仍然会给出“平坦”。 - cs95
修改问题以消除这种不明确性。 - doctorer

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