pandas - 获取另一列索引的最近值(获取另一列索引的特定列的最大值)

28

我有以下数据框:

   obj_id   data_date   value
0  4        2011-11-01  59500    
1  2        2011-10-01  35200 
2  4        2010-07-31  24860   
3  1        2009-07-28  15860
4  2        2008-10-15  200200

我想要获取数据的子集,只包含每个 'obj_id' 最近('data_date' 最大)的 'value'

我已经拼凑出了一个解决方案,但感觉很糟糕。我想知道是否有更好的方法。我相信一定有通过 Pandas 更简单的方法来实现。

我的方法基本上是分组、排序、检索和重新组合,如下:

row_arr = []
for grp, grp_df in df.groupby('obj_id'):
    row_arr.append(dfg.sort('data_date', ascending = False)[:1].values[0])

df_new = DataFrame(row_arr, columns = ('obj_id', 'data_date', 'value'))
7个回答

22
如果“obj_id”数量非常高,则需要对整个数据帧进行排序,然后删除重复项,以获取最后一个元素。
sorted = df.sort_index(by='data_date')
result = sorted.drop_duplicates('obj_id', keep='last').values

这应该会更快(抱歉我没有测试),因为您不必执行自定义聚合函数,当键值较多时它的速度很慢。您可能认为对整个数据框进行排序会更糟糕,但实际上在Python中排序很快,而本地循环很慢。


这个方法非常有效,其他答案对我来说都有问题,而且这个方法也更快。 - Kevin Dahl
对我来说,这比pdifranc的答案快了一个数量级以上。这个问题在SO上以不同的形式存在。我会把它们都指向这个答案。只是需要注意的一点:FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead - tommy.carstensen
1
在2022年查找时,您现在需要使用 sort_values 而不是 sort_index,如 @Tamelise 的答案所示。 - Stuart

18

这是另一个可能的解决方案。不确定是否是最快的(我怀疑..)因为我还没有将其与其他方法进行基准测试。

df.loc[df.groupby('obj_id').data_date.idxmax(),:]

2
这是一种不错的方法,在这个和其他情境下都适用。 - alexbw
1
一个不错的通用解决方案,但与其他一些方法相比速度较慢。 - Little Bobby Tables

5

我喜欢crewbum的答案,可能这样更快(抱歉,我还没有测试过,但是我避免了对所有内容进行排序):

df.groupby('obj_id').agg(lambda df: df.values[df['data_date'].values.argmax()])

它使用了numpy的"argmax"函数来查找最大值所在的行索引。

我在一个包含24735行的数据框上进行了速度测试,分成了16组(顺便说一下:数据集来自planethunter.org),%timeit的结果是argmax为12.5毫秒,而sort为17.5毫秒。所以两种解决方案都非常快 :-) 而且我的数据集似乎太小了 ;-) - Maximilian

4

因为tommy.carstensen指出一些函数现在存在未来警告,所以我更新了thetainted1的答案。以下是对我起作用的内容:

sorted = df.sort_values(by='data_date')

result = sorted.drop_duplicates('obj_id', keep='last')

2

在groupby对象上使用aggregate()方法,可以在单个步骤中从groupby对象创建一个新的DataFrame。(虽然我不知道有更简洁的方法来提取DataFrame的第一/最后一行。)

In [12]: df.groupby('obj_id').agg(lambda df: df.sort('data_date')[-1:].values[0])
Out[12]: 
         data_date  value
obj_id                   
1       2009-07-28  15860
2       2011-10-01  35200
4       2011-11-01  59500

您还可以对单个列执行聚合操作,此时聚合函数将在 Series 对象上工作。

In [25]: df.groupby('obj_id')['value'].agg({'diff': lambda s: s.max() - s.min()})
Out[25]: 
          diff
obj_id        
1            0
2       165000
4        34640

1
df1 = pd.DataFrame({
'Id': ['00', '01', '02', '02', '01', '03'] ,

'date': ['1990-12-31 ','1990-12-27 ','1990-12-28 ',
         '1990-12-28 ','1992-12-27 ','1990-12-30 '] , 
 
 'Population': ['700','200','300','400','500','100']        
         })
print(df1)

"""
   Id         date Population
0  00  1990-12-31         700
1  01  1990-12-27         200
2  02  1990-12-28         300
3  02  1990-12-28         400
4  01  1992-12-27         500
5  03  1990-12-30         100
"""



Max1 = df1.groupby('Id').apply( lambda df : df['Population'].values[df['Population'].values.argmax()]  )


print(Max1)

"""
Id
00    700
01    500
02    400
03    100
dtype: object
"""

Min1 = df1.groupby('Id').apply(lambda df : df['Population'].values[df['Population'].values.argmin()])

print(Min1)

"""
Id
00    700
01    200
02    300
03    100
dtype: object

"""

方法 2:

cc = df1.sort_values('Population', ascending=False).drop_duplicates(['Id'])
print(cc)

"""
   Id         date Population
0  00  1990-12-31         700
4  01  1992-12-27         500
3  02  1990-12-28         400
5  03  1990-12-30         100
"""

方法三:

aa = df1.groupby(['Id'],sort = False)['Population'].max()
print(aa)
"""
Id
00    700
01    500
02    400
03    100
Name: Population, dtype: object
"""

方法四:

res = df1.groupby(['Id'])['Population'].transform(max) == df1['Population']

print(df1[res])

"""
   Id         date Population
0  00  1990-12-31         700
3  02  1990-12-28         400
4  01  1992-12-27         500
5  03  1990-12-30         100
"""

0
我相信我找到了一个更适合的解决方案,基于这个线程中的解决方案。然而我的解决方案使用数据框的apply函数而不是聚合。它还返回一个与原始数据框相同列的新数据框。
df = pd.DataFrame({
'CARD_NO': ['000', '001', '002', '002', '001', '111'],
'DATE': ['2006-12-31 20:11:39','2006-12-27 20:11:53','2006-12-28 20:12:11','2006-12-28 20:12:13','2008-12-27 20:11:53','2006-12-30 20:11:39']})

print df 
df.groupby('CARD_NO').apply(lambda df:df['DATE'].values[df['DATE'].values.argmax()])

原文

CARD_NO                 DATE
0     000  2006-12-31 20:11:39
1     001  2006-12-27 20:11:53
2     002  2006-12-28 20:12:11
3     002  2006-12-28 20:12:13
4     001  2008-12-27 20:11:53
5     111  2006-12-30 20:11:39

返回的数据框:

CARD_NO
000        2006-12-31 20:11:39
001        2008-12-27 20:11:53
002        2006-12-28 20:12:13
111        2006-12-30 20:11:39

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