Python 从多返回值groupby apply操作输出数据帧而不是序列

Python 从多返回值groupby apply操作输出数据帧而不是序列,python,pandas,Python,Pandas,编辑: 我需要apply函数,它从几个复杂的计算中返回几个值。我可以在元组中返回这些值,因此GroupByApply操作的结果将是一个以组名为索引、以元组为值的系列。我希望它返回一个数据帧,这样我就可以保留所有的功能和灵活性 通常,groupby apply操作的结果将是一个系列,如果apply返回1个值。在apply返回2个或更多值的情况下,我希望结果是一个数据帧。所以我的问题是如何做到这一点。有关更多详细信息和示例,请参见原始Q 原始问题: 我有一个包含许多列和组的数据框架。我试图通过gro

编辑:

我需要apply函数,它从几个复杂的计算中返回几个值。我可以在元组中返回这些值,因此GroupByApply操作的结果将是一个以组名为索引、以元组为值的系列。我希望它返回一个数据帧,这样我就可以保留所有的功能和灵活性

通常,groupby apply操作的结果将是一个系列,如果apply返回1个值。在apply返回2个或更多值的情况下,我希望结果是一个数据帧。所以我的问题是如何做到这一点。有关更多详细信息和示例,请参见原始Q

原始问题:

我有一个包含许多列和组的数据框架。我试图通过groupby应用机制执行分组操作,并且只为每个组检索2个值。目前,我为每个组返回一个元组(例如,
return(a,b)
),因此我得到的结果是一个以组名为索引、以元组为值的序列

这对我来说不是最好的输出,因为我接下来需要按其中一个值进行排序,通常这样我会丢失很多数据帧和系列功能

我想取而代之的是一个包含列“a”和“b”的数据帧

例如,假设a有一个大的数据帧df,看起来像:

Out[123]:
         ID1            ID2     score
0    6073165338_1    6073165338  100
1    6073165338_1    6073165338  89
2    6073165338_1    6073165338  87
3    6073165338_1    6073165338  65
4    6073165338_1    6073165338  62
def calc(grp):
    return grp.ID2.iloc[0],grp.score[:2].mean()
我想按ID1对其进行分组,并为每个组返回ID2(每个ID1组都相同) 以及前三名参赛者的平均分。我可以这样做:

Out[123]:
         ID1            ID2     score
0    6073165338_1    6073165338  100
1    6073165338_1    6073165338  89
2    6073165338_1    6073165338  87
3    6073165338_1    6073165338  65
4    6073165338_1    6073165338  62
def calc(grp):
    return grp.ID2.iloc[0],grp.score[:2].mean()
df.groupby('ID1').apply(calc)
的结果将是一个以ID1组为索引的系列,以及以2个元素为值的元组:

6073165338_1(6073165338,94.5)

我希望输出是一个数据帧,具有与数据帧中的列相同的索引和两个值,因此我能够轻松地进行分析


我该怎么做

根据编辑后的问题,也许这就是你要找的。返回apply调用结果中的一个系列,并将其整理成一个数据帧(猜测这就是您要寻找的)

返回元组

In [721]: x.groupby('ID1').apply(lambda df: (df['ID2'].irow(0), df['score'].irow(0)))
Out[721]:
ID1
6073165338_1    (6073165338, 100)
dtype: object
返回序列

In [720]: x.groupby('ID1').apply(lambda df: pd.Series({'c1':df['ID2'].irow(0), 'c2':df['score'].irow(0)}))
Out[720]:
                  c1   c2
ID1
6073165338_1  6073165338  100

在第二个例子中,结果在一个数据帧中。好的,我有两个解决方案。第一种可能更好,不过我还是希望专家能发表评论。第一个选项是让应用的函数返回一个元组,然后将元组序列转换为数据帧:

s = x.groupby('ID1').apply(calc)
DataFrame(s.tolist(),index = s.index,columns = ['ID2','top3avg'])
这导致:

Out[156]:
                     ID2    top3avg
ID1     
6073165338_1     6073165338  94.5
第二种方法是在返回的元组上使用dataframe构造函数返回dataframe:

def calc(grp):
    return DataFrame([(grp.ID2.iloc[0],grp.score[:2].mean())],columns=['ID2','top3avg'])
x.groupby('ID1').apply(calc)
的结果现在是一个数据帧:

                         ID2    top3avg
ID1         
6073165338_1    0    6073165338  94.5
第一种选择似乎更好,因为:

  • 它只在groupby apply操作结束时运行一次DF构造函数
  • 它不会返回不必要的整数索引
    首先,看起来你的例子取了2个元素的平均值,而不是3-(100+89+87)/3=92,(100+89)/2=94.5

    对于您的示例,我认为只需在dataframe上获得简单的mean(),每个组只剩下前3行。例如,像这样:

    df.groupby('ID1').head(3).groupby('ID1').mean()
    
    例如:

    >>> df = pd.DataFrame({"ID1":['6073165338_1']*5 + [11111] * 6, "ID2":[6073165338 ]*5 + [22222] * 6, "score":[100,89,87,65,62] + [1, 2, 3, 4, 5, 6]})
    >>> df
                 ID1         ID2  score
    0   6073165338_1  6073165338    100
    1   6073165338_1  6073165338     89
    2   6073165338_1  6073165338     87
    3   6073165338_1  6073165338     65
    4   6073165338_1  6073165338     62
    5          11111       22222      1
    6          11111       22222      2
    7          11111       22222      3
    8          11111       22222      4
    9          11111       22222      5
    10         11111       22222      6
    
    >>> df.groupby('ID1').head(3).groupby('ID1').mean()
                         ID2  score
    ID1                            
    11111              22222      2
    6073165338_1  6073165338     92
    

    一个简单的例子就是我编辑了这个问题。我不能这样做,因为我得到的不是数据帧。看到编辑,我第一次可能不够清楚。添加了另一个解决方案,看看:)这肯定回答了我给出的示例,但不是我的问题。这意味着我举了一个坏例子。。假设我需要apply函数来检索一些复杂的计算。不是内置函数的函数。如果apply返回1个值,则结果为一系列。在apply返回2个或更多值的情况下,我希望结果是一个数据帧。所以我的问题是如何做到这一点。@user1945306是的,我明白了,我稍后会试试看是否能想出一些好的解决方案。