Python:检查前一个时间戳中是否存在唯一id,如果存在,则检查前一个时间戳(依此类推)

Python:检查前一个时间戳中是否存在唯一id,如果存在,则检查前一个时间戳(依此类推),python,pandas,function,dataframe,recursion,Python,Pandas,Function,Dataframe,Recursion,我有一个巨大的数据表,看起来像这样 ID Name Category Discovery Date Timestamp 1 Alpha Low 3/1/2020 7/1/2020 1 Alpha Low 4/7/2020 7/1/2020 1 Alpha Low 5/2/2020 7/1/2020 2 Bravo Medium 2/17/

我有一个巨大的数据表,看起来像这样

ID    Name   Category   Discovery Date  Timestamp
1     Alpha    Low         3/1/2020      7/1/2020
1     Alpha    Low         4/7/2020      7/1/2020
1     Alpha    Low         5/2/2020      7/1/2020
2     Bravo    Medium      2/17/2020     7/1/2020
2     Bravo    Medium      2/26/2020     7/1/2020
3     Charlie  Low         6/1/2020      7/1/2020
1     Alpha    Low         5/2/2020      7/8/2020
2     Bravo    Medium      2/17/2020     7/8/2020
3     Charlie  Low         7/11/2020     7/15/2020
我试图在发现日期和时间戳之间插入一个名为Target Date的列,如果Category=='Low',则增加90天,如果Category=='Medium',则增加30天,并对具有相同ID的所有记录使用给定时间戳内的最早发现日期。但是,需要注意的是,如果一个特定的ID在前一个时间戳(当前时段-1)中,我需要不断检查每个前一个时间戳,直到该ID没有出现在前一个时间戳中,然后可以使用最早的发现日期和类别逻辑来确定目标日期。因此,计算字段应如下所示:

ID    Name   Category   Discovery Date     Target Date       Timestamp
1     Alpha    Low         3/1/2020         5/30/2020        7/1/2020  
1     Alpha    Low         4/7/2020         5/30/2020        7/1/2020  
1     Alpha    Low         5/2/2020         5/30/2020        7/1/2020  
2     Bravo    Medium      2/17/2020        3/18/2020        7/1/2020  
2     Bravo    Medium      2/26/2020        3/18/2020        7/1/2020  
3     Charlie  Low         6/1/2020         8/30/2020        7/1/2020  
1     Alpha    Low         5/2/2020         5/30/2020        7/8/2020   
2     Bravo    Medium      2/17/2020        3/18/2020        7/8/2020  
3     Charlie  Low         7/11/2020        10/9/2020        7/15/2020

我相信最晚(在上面的例子中,7/15/2020)需要某种递归函数来启动时间戳,以检查特定的ID是否在前一个时间戳中。如果满足此条件,请检查该ID的上一个时间戳,依此类推。一旦逻辑失败,并且ID不再位于前一个时间戳中,那么您可以使用最早的发现日期和类别逻辑。

您的逻辑并不容易理解,但如果我理解了,我认为您最后一行结果的答案是不正确的,因为ID 3位于两个不同的时间戳中。无论如何,您可以根据需要使用逻辑

每个ID分组只包含一个类别,因此我没有测试两个类别在同一ID中的情况,只测试了显示的内容

这里有一种方法可以尝试,因为它匹配除最后一行之外的所有内容

def add_time(x):
    lowtardate = x['DiscoveryDate'].iat[0] + timedelta(days=90)
    x.loc[x['Category']=='Low', 'TargetDate'] = lowtardate
    medtardate = x['DiscoveryDate'].iat[0] + timedelta(days=30)
    x.loc[x['Category']=='Medium', 'TargetDate'] = medtardate
    return x
df.groupby('ID').apply(add_time)
输出:

   ID     Name Category DiscoveryDate  Timestamp TargetDate
0   1    Alpha      Low    2020-03-01 2020-07-01 2020-05-30
1   1    Alpha      Low    2020-04-07 2020-07-01 2020-05-30
2   1    Alpha      Low    2020-05-02 2020-07-01 2020-05-30
3   2    Bravo   Medium    2020-02-17 2020-07-01 2020-03-18
4   2    Bravo   Medium    2020-02-26 2020-07-01 2020-03-18
5   3  Charlie      Low    2020-06-01 2020-07-01 2020-08-30
6   1    Alpha      Low    2020-05-02 2020-07-08 2020-05-30
7   2    Bravo   Medium    2020-02-17 2020-07-08 2020-03-18
8   3  Charlie      Low    2020-07-11 2020-07-15 2020-08-30

这就是你要找的吗?我认为通过注释代码来解释它比写一个冗长的解释更容易。顺便说一句,主要思想是为每个ID识别连续的时间戳,并根据它们计算目标日期

将熊猫作为pd导入
导入日期时间
从functools导入减少,部分
#####创建示例数据帧#####
df=pd.DataFrame(
资料=[
(1、‘阿尔法’、‘低’、‘2020年1月3日’、‘2020年1月7日’,
(1、‘阿尔法’、‘低’、‘2020年7月4日’、‘2020年1月7日’,
(1、‘阿尔法’、‘低’、‘2020年5月2日’、‘2020年7月1日’,
(2、‘好极了’、‘中等’、‘2020年2月17日’、‘2020年7月1日’,
(2、‘好极了’、‘中等’、‘2020年2月26日’、‘2020年7月1日’,
(3、‘查理’、‘低’、‘2020年1月6日’、‘2020年1月7日’,
(1、‘阿尔法’、‘低’、‘2020年5月2日’、‘2020年7月8日’,
(2、‘好极了’、‘中等’、‘2020年2月17日’、‘2020年7月8日’,
(3、‘查理’、‘低’、‘2020年11月7日’、‘2020年15月7日’,
],
列=['ID'、'Name'、'Category'、'Discovery Date'、'Timestamp']
)
df['Discovery Date']=pd.to_datetime(df['Discovery Date'])
df['Timestamp']=pd.to_datetime(df['Timestamp']]
#####创建示例数据帧#####
######支持函数(暂时跳过,稍后返回)###
def get_连续_时段(时段):
“”“部分元素的列表子列表,其差值小于2。”。
因此,在本例中,在与特定ID相关联的周期中查找连续周期(又称时间戳)。
示例:句点=[1,2,3,5]->[1,2,3],[5]]
"""
p=已排序(周期)
回报率降低(
λx,y:(x[:-1]+[x[-1]+[y]])如果(y-x[-1][-1]<2)其他(x+[[y]]),则,
p[1:],
[[p[0]]],
)如果len(p)else[]
def get_最早日期(id_期间,df=None):
“”“获取每个ID和期间的最早日期”“”
返回{
元组(k):df[(df.ID==ID_periods['ID'])和(df.Period.isin(k))['Discovery Date'].min()
对于id_期间中的k['所有_期间']
}
######支持功能####
#建议的解决方案基于周期的概念,即与时间戳相关联的唯一id。
#它标识具有相同时间戳的数据批。我们假设周期值按时间排序,这意味着
#要排序的时间戳列(升序);如果不是,您可以轻松地按时间戳对数据帧进行排序:
#排序索引(by=['Timestamp'],inplace=True)
#计算与每个时间戳关联的周期
df['Period']=df['Timestamp'].diff().dt.days.astype(bool.cumsum())
#现在开始计算每个(ID,Period)对的目标日期。
#将连续周期定义为ID出现的周期的排序序列,每个周期之间的差值小于2。
#例如[3,4,5];[1,2,5]不是连续的时段
#首先,请注意,连续的周期将具有相同的最早日期,即在
#相应的样品。
# 1. 获取ID出现的所有时段
df_target_date=df.groupby(['ID'])['Period'].unique().reset_index(name='all_periods'))
# 2. 计算每个ID的连续周期
df_目标日期['all_periods']=df_目标日期['all_periods'].map(获取连续的_periods)
# 3. 获取每对的最早日期(ID、连续周期)
df_目标日期['all_periods']=df_目标日期。应用(部分(获取最早日期,df=df),轴=1)
# 4. 为每个(ID、句点)对创建具有最早日期的数据帧。
#我们将最早的日期命名为“目标日期”,只是为了简化以下任务
df_目标_日期=pd.DataFrame(
数据=减少(
λx,y:x+reduce(
lambda w,z:w+[([y.ID]+[l,z[1]])表示z[0]]中的l,
已排序(y.all_periods.items()),
[],
),
df_target_date.itertuples(),
[]
),
列=['ID'、'Period'、'Target Date'],
)
# 5. 将最早日期添加到原始数据帧
df=df.merge(df_target_date,how='left',left_on=['ID','Period'],right_on=['ID','Period']))
# 6. 下降周期列
drop(列=['Period'],inplace=True)
# 5. 按照基于类别的规则计算目标日期
df.loc[df.Category=='Low','Target Date']+=datetime.timedelta(天=90)
df.loc[df.Category=='Medium','Target Date']+=datetime.timedelta(天=30)
#重新排列列
df=df[['ID','Name','Category','Discovery Date','Target Date','Timestamp']]