【问题标题】:Pandas Efficiency -- Is there a faster way to slice thousands of times?Pandas 效率——有没有更快的方法来切片数千次?
【发布时间】:2021-02-12 03:06:59
【问题描述】:

感谢社区对此提供的任何帮助。我已经玩了几天了。

我有 2 个数据框,df1 和 df2。第一个数据帧将始终是大约 20-3 万行的 1 分钟数据。第二个数据帧将包含随机时间以及相关的相关数据,并且总是相对较小(1000-4000 行 x 4 或 5 列)。我正在使用 itertuples 处理 df1 以执行特定时间的切片(尾随)。这个过程重复了数千次,并且下面的单条切片线 (df3 = df2...) 导致了超过 50% 的运行时间。只需在下面的单行中添加几个切片标准,就可以使运行数小时的最终运行时间增加 30% 以上!

我考虑过尝试 pandas 的“查询”,但读过它真的只对更大的数据帧有帮助。我的想法是将 df2 减少为一个 numpy 数组、简单的 python 列表或其他可能会更好,因为它总是相当短,尽管我认为我需要将它放回数据框以进行后续排序、求和和向量乘法之后出现在主要代码中。我确实成功地在 12 核设置上利用了并发期货,这为我的整个应用程序提高了大约 5 倍的速度,尽管我仍在谈论运行时间。

任何帮助或建议将不胜感激。 说明问题的示例代码:

import pandas as pd 
import numpy as np
import random
from datetime import datetime as dt
from datetime import timedelta, timezone

def random_dates(start, end, n=10):
    start_u = start.value//10**9
    end_u = end.value//10**9
    return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s')

dfsize = 34000
df1 = pd.DataFrame({'datetime': pd.date_range('2010-01-01', periods=dfsize, freq='1min'), 'val':np.random.uniform(10, 100, size=dfsize)})   

sizedf = 3000
start = pd.to_datetime('2010-01-01')
end = pd.to_datetime('2010-01-24')
test_list = [5, 30]

df2 = pd.DataFrame({'datetime':random_dates(start,end, sizedf), 'a':np.random.uniform(10, 100, size=sizedf), 'b':np.random.choice(test_list, sizedf), 'c':np.random.uniform(10, 100, size=sizedf), 'd':np.random.uniform(10, 100, size=sizedf), 'e':np.random.uniform(10, 100, size=sizedf)})
df2.set_index('datetime', inplace=True)
daysback5 = 3
daysback30 = 8

#%%timeit -r1   #time this section here:
#Slow portion here - Performing ~4000+ slices on a dataframe (df2) which is ~1000 to 3000 rows -- Some slowdown due to itertuples, which don't think is avoidable
for line, row in enumerate(df1.itertuples(index=False), 0):
    if row.datetime.minute % 5 ==0: 
        #Lion's share of the slowdown:
        df3 = df2[(df2['a']<=row.val*1.25) & (df2['a']>=row.val*.75) & (df2.index<=row.datetime) & (((df2.index>=row.datetime-timedelta(days=daysback30)) & (df2['b']==30)) | ((df2.index>=row.datetime-timedelta(days=daysback5)) & (df2['b']==5))) ].reset_index(drop=True).copy()

慢部分时间:

每个循环 8.53 s ± 0 ns(平均值 ± 标准偏差,1 次运行,每个循环 1 个)

df1:
      datetime              val
0   2010-01-01 00:00:00 58.990147
1   2010-01-01 00:01:00 27.457308
2   2010-01-01 00:02:00 20.657251
3   2010-01-01 00:03:00 36.416561
4   2010-01-01 00:04:00 71.398897
... ... ...
33995   2010-01-24 14:35:00 77.763085
33996   2010-01-24 14:36:00 21.151239
33997   2010-01-24 14:37:00 83.741844
33998   2010-01-24 14:38:00 93.370216
33999   2010-01-24 14:39:00 99.720858
34000 rows × 2 columns

df2:

                               a     b          c     d   e
datetime                    
2010-01-03 23:38:13 22.363251   30  81.158073   21.806457   11.116421
2010-01-09 16:27:32 78.952070   5   27.045279   29.471537   29.559228
2010-01-13 04:49:57 85.985935   30  79.206437   29.711683   74.454446
2010-01-07 22:29:22 36.009752   30  43.072552   77.646257   57.208626
2010-01-15 09:33:02 13.653679   5   87.987849   37.433810   53.768334
... ... ... ... ... ...
2010-01-12 07:36:42 30.328512   5   81.281791   14.046032   38.288534
2010-01-08 20:26:31 80.911904   30  32.524414   80.571806   26.234552
2010-01-14 08:32:01 12.198825   5   94.270709   27.255914   87.054685
2010-01-06 03:25:09 82.591519   5   91.160917   79.042083   17.831732
2010-01-07 14:32:47 38.337405   30  10.619032   32.557640   87.890791
3000 rows × 5 columns

【问题讨论】:

  • 您是如何让您的代码在 8.5 秒内运行的?它拒绝在我的系统上几分钟内运行。
  • ??它只是在 VS Code 的 Jupyter Notebook 中。这是一台非常新的计算机,配备 AMD Ryzen 9 3900。Anaconda 环境 Python 3.7.8。我看过一些帖子说一些 Pandas 函数 w 较新的 Python 版本启用了多线程??

标签: python pandas numpy dataframe


【解决方案1】:

实际上,交叉合并和查询非常适合您的数据大小:

(df1[df1.datetime.dt.minute % 5==0].assign(dummy=1)
    .merge(df2.reset_index().assign(dummy=1),
           on='dummy', suffixes=['_1','_2'])
    .query('val*1.25 >= a >= val*.75 and datetime_2 <= datetime_1 ')
    .loc[lambda x: ((x.datetime_2 >= x.datetime_1 - daysback30) & x['b'].eq(30) )
                  |((x.datetime_2>= x.datetime_1 - daysback5) & (x['b']==5))]
)

在我的系统上发生了什么:

2.05 s ± 60.4 ms per loop (mean ± std. dev. of 7 runs, 3 loops each)

您的代码在哪里运行大约10s

【讨论】:

  • 谢谢广。这很有帮助。我将不得不继续考虑如何通过拥有这样的整个数据框来执行其余代码。上面生成了一个超过一百万行的数据框,随后迭代循环仅在日期上进行选择,相比之下非常慢。我尝试仅使用现有 iterloop 中的“查询”部分并获得了较慢的 18.5 秒——真的很喜欢语法。向我介绍了一些新功能!
猜你喜欢
  • 1970-01-01
  • 2012-02-27
  • 2021-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多