如何在pandas中使用group by对列应用“first”和“last”函数?
我有一个数据框(data frame),我想根据某一列的值来对它进行分组。简单来说,就是把数据按照某一列的内容分开。我可以这样做:grouped = df.groupby(['ColumnName'])
。
我想象这个操作的结果就像一个表格,其中有些单元格可能会包含一组值,而不是单个值。为了得到一个普通的表格(也就是每个单元格只包含一个值),我需要指定一个函数,用来把这些值的集合转换成单个值。
比如,我可以用这些值的总和来替代它们的集合,或者用最小值或最大值。我可以这样做:grouped.sum()
或者 grouped.min()
,等等。
现在,我想对不同的列使用不同的函数。我发现可以这样做:grouped.agg({'ColumnName1':sum, 'ColumnName2':min})
。
不过,由于某些原因,我不能使用 first
。具体来说,grouped.first()
可以正常工作,但 grouped.agg({'ColumnName1':first, 'ColumnName2':first})
就不行了。结果我得到了一个名称错误:NameError: name 'first' is not defined
。所以,我的问题是:为什么会这样,怎么解决这个问题。
补充说明
在这里我找到了一个例子:
grouped['D'].agg({'result1' : np.sum, 'result2' : np.mean})
也许我还需要使用 np
?但在我的情况下,Python 不认识 "np"。我需要导入它吗?
5 个回答
我不太确定这是否真的是问题所在,但 sum
和 min
是 Python 内置的函数,它们可以接收一些可迭代的对象作为输入,而 first
是 pandas 的 Series 对象的方法,所以可能在你的命名空间里找不到这个方法。此外,它的输入要求也不同(文档上说需要一个偏移值)。
我想解决这个问题的一种方法是自己创建一个 first
函数,并定义它可以接收一个 Series 对象作为输入,比如:
def first(Series, offset):
return Series.first(offset)
或者类似这样的东西……
与其使用 first
或 last
,不如在 agg
方法中使用它们的字符串表示形式。比如在提问者的例子中:
grouped = df.groupby(['ColumnName'])
grouped['D'].agg({'result1' : np.sum, 'result2' : np.mean})
#you can do the string representation for first and last
grouped['D'].agg({'result1' : 'first', 'result2' : 'last'})
我觉得问题在于有两个不同的 first
方法,它们名字相同但作用不同,一个是针对 分组对象 的,另一个是针对 Series/DataFrame 的(与时间序列有关)。
如果你想在 DataFrame 上模拟分组的 first
方法,可以使用 iloc[0]
(这个方法可以通过索引获取每个组的第一行(DataFrame/Series)):
grouped.agg(lambda x: x.iloc[0])
举个例子:
In [1]: df = pd.DataFrame([[1, 2], [3, 4]])
In [2]: g = df.groupby(0)
In [3]: g.first()
Out[3]:
1
0
1 2
3 4
In [4]: g.agg(lambda x: x.iloc[0])
Out[4]:
1
0
1 2
3 4
类似地,你可以用 iloc[-1]
来模拟 last
方法。
注意:这个方法是按列工作的,等等:
g.agg({1: lambda x: x.iloc[0]})
在旧版本的 pandas 中,你可以使用 irow 方法(例如 x.irow(0)
,请参见之前的编辑)。
这里有几个更新的说明:
使用 nth
分组方法来做这个会更好,这个方法在 0.13 版本及以上速度更快:
g.nth(0) # first
g.nth(-1) # last
你需要稍微注意一下,因为 first
和 last
的默认行为会忽略 NaN 行……如果我没记错的话,在 DataFrame 的分组中,0.13 之前这个功能是有问题的……nth
有一个 dropna
选项。
你可以使用字符串而不是内置函数(虽然如果我没记错的话,pandas 会识别出是 sum
内置函数并应用 np.sum
):
grouped['D'].agg({'result1' : "sum", 'result2' : "mean"})