可能加速 pandas 申请

possible speed up of pandas apply

我定义了两个将在我的程序中反复使用的函数:

第一个函数是将字符串转为日期时间,第二个函数是读取一个csv文件,提取事件发生前的一个值和事件后的一个值,return其余数据事件后的框架。

def to_timestamp(timestr):
   return datetime.datetime.strptime(timestr,'%H:%M:%S.%f')
def find_values(df,ticker,event_time):
   df=pd.read_csv(ticker+'.csv',sep=',')
   df['Time'] = df['Timestamp'].apply(to_timestamp)
   df_earlier = df[df['Time']<=newstime]
   df_later = df[df['Time']>newstime]
   price_1=df_earlier['Price'].iloc[-1]
   price_2=df_later['Price'].iloc[0]
   return (price_1,Price_2,df_later)

csv 文件格式如下:

     Timestamp,       Price 
   04:15:01.274, 35.50
   04:15:01.353, 35.71
   04:15:05.184, 37.37
   05:36:25.240, 37.60
   05:44:40.678, 36.51
   …

这两个函数都有效,但如果我在数千个 csv 文件上使用它们,它们会非常慢。我认为主要的瓶颈是 apply 方法。有没有办法加快速度?谢谢

def find_values(ticker, event_time):
    filename = ticker+'.csv'
    df = pd.read_csv(filename, parse_dates=[0])
    idx = df['Timestamp'].searchsorted(event_time, side='right')
    price_1, price_2 = df['Price'].iloc[idx-1:idx+1]
    df_later = df.iloc[idx:]
    return price_1, price_2, df_later

例如,使用您发布的数据:

In [176]: p1, p2, df_later = find_values('ABC', pd.Timestamp('4:15:03'))

In [177]: p1, p2
Out[177]: (35.710000000000001, 37.369999999999997)

In [178]: df_later
Out[178]: 
                   Timestamp  Price
2 2015-01-19 04:15:05.184000  37.37
3 2015-01-19 05:36:25.240000  37.60
4 2015-01-19 05:44:40.678000  36.51

如果 csv 很大,解析 csv 的成本可能很高。因此,您不想 如果可以的话,请多次致电 pd.read_csv。推而广之,你 不应为每个代码调用 find_values 多次。如果你确实需要 为同一个代码多次调用 find_values,需要考虑一下 研究如何重新设计算法,以便理想地调用 pd.read_csv 只有一次。缓存 pd.read_csv 返回的值可能是一种方式,或者 将 event_times 收集到对 find_values 的一次调用中可能是另一个

现在假设您已经在节俭地调用 find_values,让我们继续讨论如何提高它的速度。

你是对的,在这里使用 apply 也是一个潜在的瓶颈,因为它为数据帧的每一行调用一次 Python 函数。您可以使用 pd.read_csv 的内置日期字符串解析功能,而不是使用 to_timestamp 解析时间字符串:

df = pd.read_csv(filename, parse_dates=[0])

这会将第 0 个索引列解析为日期字符串。这将使 df['Timestamp'] 数据类型为 datetime64[ns] 的列。 这太棒了,因为它可以很容易地找到 event_time(我假设它与 newstime 相同)适合 df['Timestamp'] 的索引。此外,与在 Python datetime.datetime 对象上进行的等效计算相比,在 datetime64s 上执行日期计算通常要快得多。

要找到适合 event_time 的整数索引,请使用 the searchsorted method:

idx = df['Timestamp'].searchsorted(event_time)

idx 将是 event_time 所在的整数索引,如果它被插入到 df['Timestamp'] 中,同时保持 df['Timestamp'] 的排序。


接下来,注意使用

df_earlier = df[df['Time']<=newstime]

也很昂贵,因为它形成一个(可能很大的)数据框只是为了挑选一个值。由于 df['Time']<=newstime 是一个布尔掩码,这个新数据帧 df[df['Time']<=newstime]df 中复制了一个 copy 数据。这是很多不必要的复制。

相反,您可以使用

price_1, price_2 = df['Price'].iloc[idx-1:idx+1]

无需大量额外复制即可仅选择所需的值。

终于可以使用

df_later = df.iloc[idx:]

定义df_later。由于这使用基本切片而不是布尔掩码,因此 df_later df 的视图 。这比 df[df['Time']>event_time] 生成速度更快,因为没有复制。但也要注意,这意味着 df_later 中的基础数据与 df 中的基础数据完全相同。因此,修改 df_later 也会修改 df,反之亦然。如果你不想 df_later 成为视图,那么使用

df_later = df.iloc[idx:].copy()