有没有办法在午夜过后的秒数记录上加速日期解析器?

Is there a way to accelerate date parser on seconds-past-midnight records?

假设我有以下数据集,在 date_str 列中包含日期 (str),在 seconds_past_midnight 列中包含午夜后的秒数 (int)。我想根据这两列的组合来解析日期时间。

import pandas as pd
import numpy as np

n = 1000000
df = pd.DataFrame({'seconds_past_midnight': np.random.randint(34200, 57601, size=n), 'date_str': ['2015-07-14']*n})

print(df)

          date_str  seconds_past_midnight
0       2015-07-14                  48642
1       2015-07-14                  39170
2       2015-07-14                  43940
3       2015-07-14                  46927
4       2015-07-14                  55376
5       2015-07-14                  35859
6       2015-07-14                  38705
7       2015-07-14                  35932
8       2015-07-14                  36874
9       2015-07-14                  39487
...            ...                    ...
999990  2015-07-14                  54837
999991  2015-07-14                  47146
999992  2015-07-14                  54188
999993  2015-07-14                  54729
999994  2015-07-14                  35574
999995  2015-07-14                  35815
999996  2015-07-14                  38727
999997  2015-07-14                  38374
999998  2015-07-14                  53055
999999  2015-07-14                  43303

[1000000 rows x 2 columns]

print(df.dtypes)

date_str                 object
seconds_past_midnight     int64
dtype: object

我能想到的最直接的方法是根据这些秒数构造 pd.Timedelta 并将它们添加到日期对象中,但这本质上是使用 [=16= 时的逐行 for 循环] 做转换,这很慢。

%time df.apply(lambda row: pd.to_datetime(row.date_str) + pd.Timedelta(row.seconds_past_midnight, 's'), axis=1)


CPU times: user 2min 5s, sys: 311 ms, total: 2min 5s
Wall time: 2min 5s

所以我想知道是否有办法加速这个过程?也许我不知道 datetime 对象上的一些矢量化函数?我认为稍微提高速度的一种方法是使用 multiprocessing 模块,也许我可以期望在 8 核 PC 上快 4-6 倍。另外,因为我在 apply 中调用 python 函数,cython 或 jit 在这种情况下没有帮助?

您可以解析它 strptime("%Y-%m-%d %f"),%f 在技术上是微秒,不知道这是否有帮助?

dates = pd.to_datetime(df.date_str) + pd.to_timedelta(df.seconds_past_midnight, unit='s')

pd.to_datetimepd.to_timedelta 都已经矢量化了。

In [13]: np.random.seed(1234)

In [14]: df = pd.DataFrame({'seconds_past_midnight': np.random.randint(34200, 57601, size=n), 'date_str': ['2015-07-14']*n})

In [15]: df.head()
Out[15]: 
     date_str  seconds_past_midnight
0  2015-07-14                  35518
1  2015-07-14                  51248
2  2015-07-14                  56721
3  2015-07-14                  57417
4  2015-07-14                  42671

In [16]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1000000 entries, 0 to 999999
Data columns (total 2 columns):
date_str                 1000000 non-null object
seconds_past_midnight    1000000 non-null int64
dtypes: int64(1), object(1)
memory usage: 22.9+ MB

In [17]: (pd.to_datetime(df['date_str']) + pd.to_timedelta(df['seconds_past_midnight'],unit='s')).head()
Out[17]: 
0   2015-07-14 09:51:58
1   2015-07-14 14:14:08
2   2015-07-14 15:45:21
3   2015-07-14 15:56:57
4   2015-07-14 11:51:11
dtype: datetime64[ns]

In [18]: %timeit pd.to_datetime(df['date_str']) + pd.to_timedelta(df['seconds_past_midnight'],unit='s')        
10 loops, best of 3: 187 ms per loop

这是当前的主人,它有一些性能改进。在 0.16.2 中,这慢了 2 倍。

你也可以用NumPy datetime64's and timedelta64's做加法:

(np.array(df['date_str'], '<M8[D]') + 
np.array(df['seconds_past_midnight'], dtype='<m8[s]'))

例如,

import pandas as pd
import numpy as np
np.random.seed(1234)

n = 1000000
df = pd.DataFrame({
    'seconds_past_midnight': np.random.randint(34200, 57601, size=n), 
    'date_str': ['2015-07-14']*n})

包括对 DataFrame 的赋值:

In [4]: pd.__version__
Out[6]: u'0.16.2+175.g5a9a9da'

In [7]: %timeit df['date'] = np.array(df['date_str'], '<M8[D]')+np.array(df['seconds_past_midnight'], dtype='<m8[s]')
10 loops, best of 3: 94.6 ms per loop

In [8]: %timeit df['date2'] = pd.to_datetime(df['date_str']) + pd.to_timedelta(df['seconds_past_midnight'],unit='s')  
10 loops, best of 3: 188 ms per loop

In [12]: df['date'].equals(df['date2'])
Out[12]: True

(没有分配给DataFrame,datetime64/timedelta64和returns一个NumPy数组,而to_datetime/to_timedelta和returns一个Pandas系列所以比较这些将是苹果与橘子的比较。)