有没有办法对这个 pandas 应用方法进行矢量化以使代码运行得更快?

Posted

技术标签:

【中文标题】有没有办法对这个 pandas 应用方法进行矢量化以使代码运行得更快?【英文标题】:Is there a way to vectorize this pandas apply method to make the code run faster? 【发布时间】:2020-06-16 09:26:38 【问题描述】:

目前运行约 220K 行需要 40 - 50 分钟

shop    timestamp   flag
10061   1577525275  NaN
10061   1577534732  NaN
10061   1577741715  NaN
10061   1577741800  NaN
10084   1577405286  NaN
def foo(row):
    criteria = (pd.isnull(df2.flag)) & (df.shop==row.shop) & (abs(df.timestamp-row.timestamp) <= 3600)
    df2.loc[criteria, 'flag'] = 1
df2 = df.copy()
df2.apply(foo, axis=1)

我正在对 df2 中的每一行进行检查,我正在检查主 df 以查看是否在同一家商店中,其他行在一小时内有时间戳并将标志设置为 1。是否有如何使用 numpy 向量使其运行更快?

预期输出:

shop    timestamp   flag
10061   1577525275  NaN
10061   1577534732  NaN
10061   1577741715  1
10061   1577741800  1
10084   1577405286  NaN

【问题讨论】:

发布预期输出时会有所帮助 【参考方案1】:

要更快地完成任务,请定义以下函数:

def newFlag(grp):
    tt = grp.timestamp
    ind = np.nonzero(np.triu(np.absolute(tt[np.newaxis, :] - tt[:, np.newaxis]) <= 3600, 1))
    tbl = grp.flag.values
    tbl[np.concatenate(ind)] = 1
    return pd.Series(np.where(np.isnan(grp.flag), tbl, grp.flag), index=grp.index)

然后应用它,将结果保存在 flag 列中:

df['flag'] = df.groupby('shop').apply(newFlag).reset_index(level=0, drop=True)

这个解决方案的速度是基于shop分组的,所以你不必 比较有关不同商店的行。

另一个与速度有关的重要因素是 Numpy 函数的使用, 它的运行速度比 Pandas 快得多。

要完全理解所有细节,请为选定的组逐步运行此代码 行数(针对特定的shop)并查看结果。

【讨论】:

这太棒了。我从没想过——这只运行了 10 秒。我想它正在使用矩阵减法?因为它是行向量减去 col 向量,所以它与所有内容都不同,并且 groupby 确保所有商店行!我不明白为什么是 np.triu?是因为它是对称的吗? 需要使用 triu 来迭代差异结果的上三角部分(你是对的,它是对称的)。另请注意,k 参数设置为 1,以省略对角线,其中每个元素为 0(与第 号元素的距离x 到它自己)。 有没有办法用增量编号标记记录?例如现在它的标志全为 1,但我不知道哪些订单被分组为一小时间隔。 您的帖子不包含对此事的任何要求。也许你应该先按 shop 然后按 timestamp 对行进行排序?【参考方案2】:

您需要按商店对数据框进行分组,然后按时间戳对每个组进行排序,最后只检查上一行和下一行:

print(df)
    shop   timestamp
0  10061  1577525275
1  10061  1577534732
2  10061  1577741715
3  10061  1577741800
4  10084  1577405286


def have_similar(df):
    df = df.sort_values('timestamp')
    df = df.assign(
        flag = (df.timestamp - df.timestamp.shift(1) < 3600) |
               (df.timestamp.shift(-1) - df.timestamp < 3600)
    )
    return df

result = df.groupby('shop').apply(have_similar).reset_index(drop=True)


print(result)
    shop   timestamp   flag
0  10061  1577525275  False
1  10061  1577534732  False
2  10061  1577741715   True
3  10061  1577741800   True
4  10084  1577405286  False

【讨论】:

以上是关于有没有办法对这个 pandas 应用方法进行矢量化以使代码运行得更快?的主要内容,如果未能解决你的问题,请参考以下文章

我如何在熊猫中对这个操作进行矢量化?

有没有办法使用 python pandas 进行分组?

为啥 Pandas 应用可以比矢量化内置函数更快 [重复]

pandas数组(pandas Series)-NaN的处理

有没有一种简单的方法可以从布尔表达式中从 pandas DataFrame 中提取行?

将熊猫数据框转换为矢量[重复]