加速向前看,在数据框架中的timeseries搜索后面

2024-05-16 10:46:58 发布

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

我有一个熊猫数据框架,包括一个电子商务网站的项目。我正在使用如下函数检查数据框中的项是否在随后的(向前看)或之前的(向后看)天仍然存在:

#get a unique set of product urls for the next 2 days. If the current row's URL is in that set, mark it as 0 (not expiring); otherwise mark it 1 (expiring).

def is_expiring_product(row):
    if row.childurl in df[(df['date'] > row['date']) & (df['date'] < (row['date']  + np.timedelta64(2,"D")) ) ].childurl.unique():
        row.is_expiring_product = 0
    else:
        row.is_expiring_product = 1
    return row

问题是,这需要时间(24小时以上,如果它完成的话)才能应用到一帧超过1M的记录上。你知道吗

直觉告诉我一定有更有效的方法。。。你知道吗

也许为每一天创建一个单独的包含唯一URL的数据帧,然后在该数据帧上而不是在更大的帧上搜索?我不知道为什么会更快。。。你知道吗


Tags: the数据inurldfdateisit
1条回答
网友
1楼 · 发布于 2024-05-16 10:46:58

代码#1的示例:Compute是否为单个日期到期产品

对于测试,我使用了以下数据帧(date列的类型是datetime64[ns]):

        date  url
0 2019-09-10  Xxx
1 2019-09-12  Xxx
2 2019-09-14  Xxx
3 2019-09-11  Yyy
4 2019-09-12  Yyy
5 2019-09-12  Zz1
6 2019-09-12  Zz2
7 2019-09-08  Ttt
8 2019-09-12  Ttt
9 2019-09-15  Ttt

起点是定义两个变量,用于切割df, 按日期,放入垃圾箱(过去今天未来):

dMin = pd.to_datetime(0)            # "Minimal" date
dMax = pd.to_datetime('2050-12-31') # "Maximal" date

假设我们只计算2019-09-12年的到期产品, 因此,首先要设置currDate(也将用于切割):

currDate = pd.to_datetime('2019-09-12')

然后在addis\u expiring\u product列中,首先填充空字符串:

df = df.assign(is_expiring_product='')

计算-将日期切割到桶中的结果:

bucket = pd.cut(df.date, bins = [dMin, currDate - pd.offsets.Day(1),
    currDate, dMax], labels=['Past', 'Today', 'Future'])

下一步是计算dfu current的一个子集 “当前”日期:

df_current = df[bucket == 'Today']

要为“其他”日期在行中生成urls的索引,请运行:

ind_other = pd.Index(df[bucket.isin(['Past', 'Future'])].url.unique())

最后一步是为is\u expiring\u product生成值 更新此列:

df.is_expiring_product.update((~df_current.url.isin(ind_other)).astype(int))

结果是:

        date  url is_expiring_product
0 2019-09-10  Xxx                    
1 2019-09-12  Xxx                   0
2 2019-09-14  Xxx                    
3 2019-09-11  Yyy                    
4 2019-09-12  Yyy                   0
5 2019-09-12  Zz1                   1
6 2019-09-12  Zz2                   1
7 2019-09-08  Ttt                    
8 2019-09-12  Ttt                   0
9 2019-09-15  Ttt                    

结果说明:

  • 非空值用于2019-09-12。你知道吗
  • 索引==14120-这些urls(XxxYyyTtt) 在其他日期出席。你知道吗
  • 索引==561-这些urls(Zz1Zz2)不是 在其他日期出席。你知道吗

代码#2的示例:Compute是否为所有日期的产品过期

源数据帧是相同的,从定义dMindMax开始 和以前一样。你知道吗

然后定义以下函数来设置过期状态:

def setExpired(grp):
    currDate = grp.name    # Grouping key
    bucket = pd.cut(df.date, bins = [dMin, currDate - pd.offsets.Day(1),
        currDate, dMax], labels=['Past', 'Current', 'Future'])
    df_current = df[bucket == 'Current']
    ind_other = pd.Index(df[bucket.isin(['Past', 'Future'])].url.unique())
    res = (~df_current.url.isin(ind_other)).astype(int)
    return res

整个计算归结为一条指令, 将此功能应用于每个组(每个日期):

df['is_expiring_product'] = df.groupby('date').apply(setExpired).droplevel(0)

需要额外调用droplevel(0),因为 应用具有多索引

  • 0级-分组日期
  • 级别1-调用setExpired返回的每个系列的键。你知道吗

因此,为了保存在df(使用“普通”索引),顶层 必须删除多重索引的。你知道吗

这次的结果是:

        date  url  is_expiring_product
0 2019-09-10  Xxx                    0
1 2019-09-12  Xxx                    0
2 2019-09-14  Xxx                    0
3 2019-09-11  Yyy                    0
4 2019-09-12  Yyy                    0
5 2019-09-12  Zz1                    1
6 2019-09-12  Zz2                    1
7 2019-09-08  Ttt                    0
8 2019-09-12  Ttt                    0
9 2019-09-15  Ttt                    0

另一个更简单、更快的解决方案

请注意,不需要为计算产品是否过期 每行分开。你知道吗

更简单、更快、完全不同的解决方案是:

  • compute是每个url的到期产品(不重复)
  • 然后将结果保存在包含此url的所有行中。你知道吗

此解决方案的工作原理如下:

  • url对行进行分组。你知道吗
  • 对于每组:
    • 日期
    • 检查所有日期是否相同
    • 如果是,则返回1(此url仅在单个日期发生)
    • 否则返回0(此url发生在多个日期)。你知道吗
  • 在包含此url的每一行上“展开”此结果。你知道吗
  • 将结果保存在is\u expiring\u product列中。你知道吗

上述整个操作可另存为一条指令:

df['is_expiring_product'] = df.groupby('url').date\
    .transform(lambda grp: 1 if grp.unique().size == 1 else 0)

结果和以前一样。你知道吗

请写一个评论,执行上面的内容需要多长时间 数据帧上的指令以及它有多少行。你知道吗

相关问题 更多 >