我定义了两个函数,它们将在我的程序中反复使用:
第一个函数是将字符串转换为日期时间,第二个函数是读取 csv 文件并提取事件发生前的一个值和事件发生后的一个值,并返回事件后的其余数据帧。
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 函数。而不是使用 to_timestamp
解析时间字符串,您可以改为使用 pd.read_csv
内置的日期字符串解析能力:
df = pd.read_csv(filename, parse_dates=[0])
这会将第 0 个索引列解析为日期字符串。这将使
df['Timestamp']
dtype datetime64[ns]
的列。
这太棒了,因为它可以找到索引 event_time
(我认为与 newstime
相同)适合 df['Timestamp']
好简单。此外,日期计算在 datetime64 上执行的速度通常比在 Python 上执行的等效计算快得多 datetime.datetime
对象。
查找整数索引,其中 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
是一个 bool 掩码,这个新数据框 df[df['Time']<=newstime]
复制 df
中的数据副本 。这是很多不必要的复制。
相反,您可以使用
price_1, price_2 = df['Price'].iloc[idx-1:idx+1]
只选择您想要的值,无需进行大量额外的复制。
最后,你可以使用
df_later = df.iloc[idx:]
定义df_later
。由于这使用基本切片而不是 bool 掩码,df_later
是 df
的 View 。生成速度比 df[df['Time']>event_time]
更快因为没有复制。但也要注意,这意味着 df_later
中的基础数据df
的底层数据完全相同。因此,修改 df_later
还修改 df
反之亦然。如果您不想要df_later
成为一个 View ,然后使用
df_later = df.iloc[idx:].copy()
关于python - pandas 应用的可能加速,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28036288/