在pandas数据框中选择每个组中的最新数据。

133
如何对pandas数据框的值进行分组,并从每个组中选择最新的(按日期)?
例如,给定按日期排序的数据框:
    id     product   date
0   220    6647     2014-09-01 
1   220    6647     2014-09-03 
2   220    6647     2014-10-16
3   826    3380     2014-11-11
4   826    3380     2014-12-09
5   826    3380     2015-05-19
6   901    4555     2014-09-01
7   901    4555     2014-10-05
8   901    4555     2014-11-01

按照id或产品进行分组,并选择最新的结果:
    id     product   date
2   220    6647     2014-10-16
5   826    3380     2015-05-19
8   901    4555     2014-11-01
6个回答

164

您也可以使用groupbytail来获取组的最后n个值:

df.sort_values('date').groupby('id').tail(1)

    id  product date
2   220 6647    2014-10-16
8   901 4555    2014-11-01
5   826 3380    2015-05-19

10
我喜欢这个,因为它不仅适用于约会。 - scottlittle
1
这个选项比被接受的答案快得多,但可读性较差。 而且,假设 groupby 保留顺序,这不是一个问题吗? - Michael D
4
groupby 保留原有的顺序,详见 https://dev59.com/418d5IYBdhLWcg3wxUjC。 - Martien Lubberink
@ade1e 如果要进行重新采样(比如每月或每年)并保留组中最后的n个值,而不是求和/平均,该如何更改代码? - Andreuccio
2
我觉得这个答案比被采纳的那个更易读 @MichaelD :) - Mr_and_Mrs_D
对于不喜欢groupby的人:df.sort_values('date').drop_duplicates('id', keep='last') - Alex Li

75
使用 groupby 中的 idxmax 并使用 locdf 进行切片。
df.loc[df.groupby('id').date.idxmax()]

    id  product       date
2  220     6647 2014-10-16
5  826     3380 2015-05-19
8  901     4555 2014-11-01

8
该解决方案对于数百万条记录的处理速度非常缓慢。 - Hardik Gupta
2021年我遇到了这个错误:KeyError:“不再支持将列表式传递给.loc或[],并且存在任何缺失标签。” - kame
请使用reindex。 - piRSquared
如果日期列有“NaT”,这个方法是否无法正常工作? - Naveen Reddy Marthala

38

我曾经遇到类似的问题,最终使用了drop_duplicates而不是groupby

在大型数据集上,与上述其他方法相比,它似乎运行速度更快。

df.sort_values(by="date").drop_duplicates(subset=["id"], keep="last")

    id  product        date
2  220     6647  2014-10-16
8  901     4555  2014-11-01
5  826     3380  2015-05-19

4
我也通常使用这种方法,但希望更快的解决方案能通过使用groupby来实现。groupby在直觉上更容易理解,通常也是我们解决这个问题的思路! - rmilletich
然而,这种方法仅适用于您想要在每个组中保留1条记录,而不是使用tail时保留N条记录,如@nipy的答案所示。 - npetrov937

26

假设你已经有了一个按日期排序的数据框,你可以通过多种方式获得所需信息:

比如这样:

df.groupby(['id','product']).last()

就像这样:

df.groupby(['id','product']).nth(-1)

或像这样:

df.groupby(['id','product']).max()

如果你不想让idproduct作为索引出现,可以使用groupby(['id', 'product'], as_index=False)。或者可以尝试以下方法:

groupby(['id', 'product']).reset_index()

df.groupby(['id','product']).tail(1)

7
在我的测试中,当同一列中存在 None 值时,last() 的行为与 nth() 有些不同。例如,如果一组中的第一行具有值 1,而同一组中其余行都为 None,则 last() 将返回值 1,即使最后一行是 None。另一方面,nth(-1) 将返回 None,更符合我的预期。 - Canol Gökel

4

要使用.tail()作为聚合方法并保持分组不变:

df.sort_values('date').groupby('id').apply(lambda x: x.tail(1))

        id  product date
id              
220 2   220 6647    2014-10-16
826 5   826 3380    2015-05-19
901 8   901 4555    2014-11-01

0
#import datetime library
from datetime import datetime as dt

#transform the date column to ordinal, or create a temp column converting to ordinal.
df['date'] = df.date.apply(lambda date: date.toordinal())

#apply aggregation function depending your desire. Earliest or Latest date.
latest_date = df.groupby('id').agg(latest=('date', max)) 
earliest_date = df.groupby('id').agg(earliest=('date', min)) 

#convert it from ordinal back to date.
df['date'] = df.date.apply(lambda date: dt.fromordinal(date))


#This operation may take seconds on millions of records.

当然,您不需要执行此转换才能找到最新或最早的日期。 - AlexK

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