如何使用groupby和lamda表达式为.loc指定日期范围?

2024-04-20 09:56:22 发布

您现在位置:Python中文网/ 问答频道 /正文

我正试图找出如何为唯一的用户ID获取前30-60天的过滤数据帧。我能够用下面的代码获得前30天

get_first_month = get_first_90.loc[df.groupby('User ID')['Date'].apply(lambda g: g <= g.min() + timedelta(days=30))]

但我没有弄清楚如何指定30-60天。我试过:

get_first_month = get_first_90.loc[df.groupby('User ID')['Date'].apply(lambda g: g.min() + timedelta(days30) > g <= g.min() + timedelta(days60))]

但它返回一个关于序列的真值不明确的错误。我也尝试过一些其他的方法,但是我想不出来。谢谢你的时间


Tags: lambda用户iddfgetdateminloc
2条回答

您应该使用groupby+transformmin最小日期广播回该用户的每一行。然后,您可以为整个数据帧创建一个简单的掩码,检查它是否是between最小日期和一些偏移量。(这里我将使用2天和3天,但您可以轻松地将其更改为30天和60天,以获得真实数据)

样本数据

import pandas as pd
df = pd.DataFrame({'User ID': ['A']*5+['B']*7,
                   'Date': pd.date_range('2010-01-01', freq='1D', periods=12)})

代码

# Earliest Date for each `User ID`
s = df.groupby('User ID')['Date'].transform('min')

# Boolean mask of dates between 2 and 3 days (inclusive) after the earliest date 
m = df['Date'].between(s+pd.offsets.DateOffset(days=2), 
                       s+pd.offsets.DateOffset(days=3))

df.loc[m]

#  User ID       Date
#2       A 2010-01-03
#3       A 2010-01-04
#7       B 2010-01-08
#8       B 2010-01-09

为了完整起见,下面是分配回数据帧时掩码的外观

df['select'] = m

#   User ID       Date  select
#0        A 2010-01-01   False
#1        A 2010-01-02   False
#2        A 2010-01-03    True
#3        A 2010-01-04    True
#4        A 2010-01-05   False
#5        B 2010-01-06   False
#6        B 2010-01-07   False
#7        B 2010-01-08    True
#8        B 2010-01-09    True
#9        B 2010-01-10   False
#10       B 2010-01-11   False
#11       B 2010-01-12   False

行也不需要完全是日期。只要它在[min_datetime+2天,min_datetime+3天]之间,就会被选中

  • 在条件列表中指定最小和最大日期
    • g.min() + pd.Timedelta(days=30)
    • g.min() + pd.Timedelta(days=60)
    • 相当于date + 30 <= date <= date + 60
    • 注意,每个条件必须在(...) & (...)中,这就是问题中的实现不起作用的原因
  • 使用^{},因此datetime中的timedelta不需要导入
  • 这个解决方案修复了您当前的代码,但是,来自ALollzsolution是如何实现的。该解决方案使用带有Boolean Indexing的矢量化方法,并且比使用.apply要快得多
import pandas as pd
import random  # just for test data

# setup test data for example
random.seed(365)
data = {'User ID': [random.choice(['A', 'B', 'C', 'D', 'E']) for _ in range(90)],
        'Date': pd.bdate_range('2020-09-20', freq='d', periods=90).tolist()}
df = pd.DataFrame(data)

# selected data; 
between_30_60 = df.loc[df.groupby('User ID')['Date'].apply(lambda g: (g >= g.min() + pd.Timedelta(days=30)) & (g <= g.min() + pd.Timedelta(days=60)))]

# display(between_30_60)
   User ID       Date
32       B 2020-10-22
33       C 2020-10-23
34       E 2020-10-24
35       C 2020-10-25
36       B 2020-10-26
37       E 2020-10-27
38       B 2020-10-28
39       B 2020-10-29
41       A 2020-10-31
42       C 2020-11-01
43       C 2020-11-02
44       E 2020-11-03
45       D 2020-11-04
46       B 2020-11-05
47       D 2020-11-06
48       A 2020-11-07
49       C 2020-11-08
50       D 2020-11-09
51       C 2020-11-10
52       B 2020-11-11
53       E 2020-11-12
54       D 2020-11-13
55       B 2020-11-14
56       A 2020-11-15
57       C 2020-11-16
58       D 2020-11-17
59       C 2020-11-18
60       D 2020-11-19
61       A 2020-11-20
65       D 2020-11-24
68       A 2020-11-27
71       A 2020-11-30

相关问题 更多 >