更快 datetime.strptime
Faster datetime.strptime
正在尝试从数百万 bytes
个对象中获取 unixtimestamp
使用这个
import datetime
dt_bytes = b'2019-05-23 09:37:56.362965'
#fmt = '%m/%d/%Y %H:%M:%S.%f'
fmt = '%Y-%m-%d %H:%M:%S.%f'
dt_ts = datetime.datetime.strptime(dt_bytes.decode('utf-8'), fmt)
unix_ts = dt_ts.timestamp()
完美运行:
In [82]: unix_ts
Out[82]: 1558604276.362965
但是 decode('utf-8')
将流量减半(从 38k/sec 到 20k/sec)。
那么有没有办法 从 bytes
输入而不是 str
输入获取 unixtimestamp?
__UPDATE:__
我发现瓶颈是datetime.datetime.strptime(..)
,所以我切换到np.datetime64
()
__UPDATE 2:__
检查下面接受的答案以获得不同方法的良好性能基准。
我要转到 numpy.datetime64
因为它的延迟比 datetime.strptime
少
import numpy as np
# This is the format np.datetime64 needs:
#np.datetime64('2002-06-28T01:00:00.000000000+0100')
dt_bytes = b'2019-05-23 09:37:56.362965'
#dt_bytes_for_np = dt_bytes.split(b' ')[0] + b'T' + dt_bytes.split(b' ')[1]
dt_bytes_for_np = dt_bytes.replace(b' ', b'T')
ts = np.datetime64(dt_bytes_for_np)
并获取 unixtimestamp(这增加了一点延迟,但仍然比 datetime.strptime
:
ts.astype('datetime64[ns]').astype('float') / 1000000000
1558604276.362965
我们首先假设您有 ISO 格式的字符串,'%Y-%m-%dT%H:%M:%S.%f',在 list
中(我们也不考虑解码现在来自字节数组):
from datetime import datetime, timedelta
base, n = datetime(2000, 1, 1, 1, 2, 3, 420001), 1000
datelist = [(base + timedelta(days=i)).isoformat(' ') for i in range(n)]
# datelist
# ['2000-01-01 01:02:03.420001'
# ...
# '2002-09-26 01:02:03.420001']
从字符串到日期时间对象
让我们使用不同的方法定义一些将字符串解析为 datetime
的函数:
import re
import numpy as np
def strp_isostr(l):
return list(map(datetime.fromisoformat, l))
def isostr_to_nparr(l):
return np.array(l, dtype=np.datetime64)
def split_isostr(l):
def splitter(s):
tmp = s.split(' ')
tmp = tmp[0].split('-') + [tmp[1]]
tmp = tmp[:3] + tmp[3].split(':')
tmp = tmp[:5] + tmp[5].split('.')
return datetime(*map(int, tmp))
return list(map(splitter, l))
def resplit_isostr(l):
# return list(map(lambda s: datetime(*map(int, re.split('T|-|\:|\.', s))), l))
return [datetime(*map(int, re.split('\ |-|\:|\.', s))) for s in l]
def full_stptime(l):
# return list(map(lambda s: datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f'), l))
return [datetime.strptime(s, '%Y-%m-%d %H:%M:%S.%f') for s in l]
如果我 运行 %timeit
在 IPython 控制台中使用我机器上的这些功能,我得到
%timeit strp_isostr(datelist)
98.2 µs ± 766 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit isostr_to_nparr(datelist)
1.49 ms ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit split_isostr(datelist)
3.02 ms ± 236 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit resplit_isostr(datelist)
3.8 ms ± 256 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit full_stptime(datelist)
16.7 ms ± 780 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
因此我们可以得出结论,内置 datetime.fromisoformat
是目前为止最快的选项 对于 1000 个元素的输入。但是,这假设您希望使用 list
。无论如何,如果您需要 np.array
或 datetime64
,那么直接使用它似乎是最好的选择。
第三方选项:ciso8601
如果您能够安装额外的软件包,ciso8601
值得一看:
import ciso8601
def ciso(l):
return list(map(ciso8601.parse_datetime, l))
%timeit ciso(datelist)
138 µs ± 1.83 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
从日期时间对象到纪元以来的秒数
查看从 datetime
对象到 POSIX 时间戳的转换,使用最明显的 datetime.timestamp
方法似乎是最有效的:
import time
def dt_ts(l):
return list(map(datetime.timestamp, l))
def timetup(l):
return list(map(time.mktime, map(datetime.timetuple, l)))
%timeit dt_ts(strp_isostr(datelist))
572 µs ± 4.57 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit timetup(strp_isostr(datelist))
1.44 ms ± 15.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
正在尝试从数百万 bytes
个对象中获取 unixtimestamp
使用这个
import datetime
dt_bytes = b'2019-05-23 09:37:56.362965'
#fmt = '%m/%d/%Y %H:%M:%S.%f'
fmt = '%Y-%m-%d %H:%M:%S.%f'
dt_ts = datetime.datetime.strptime(dt_bytes.decode('utf-8'), fmt)
unix_ts = dt_ts.timestamp()
完美运行:
In [82]: unix_ts
Out[82]: 1558604276.362965
但是 decode('utf-8')
将流量减半(从 38k/sec 到 20k/sec)。
那么有没有办法 从 bytes
输入而不是 str
输入获取 unixtimestamp?
__UPDATE:__
我发现瓶颈是datetime.datetime.strptime(..)
,所以我切换到np.datetime64
(
__UPDATE 2:__ 检查下面接受的答案以获得不同方法的良好性能基准。
我要转到 numpy.datetime64
因为它的延迟比 datetime.strptime
import numpy as np
# This is the format np.datetime64 needs:
#np.datetime64('2002-06-28T01:00:00.000000000+0100')
dt_bytes = b'2019-05-23 09:37:56.362965'
#dt_bytes_for_np = dt_bytes.split(b' ')[0] + b'T' + dt_bytes.split(b' ')[1]
dt_bytes_for_np = dt_bytes.replace(b' ', b'T')
ts = np.datetime64(dt_bytes_for_np)
并获取 unixtimestamp(这增加了一点延迟,但仍然比 datetime.strptime
:
ts.astype('datetime64[ns]').astype('float') / 1000000000
1558604276.362965
我们首先假设您有 ISO 格式的字符串,'%Y-%m-%dT%H:%M:%S.%f',在 list
中(我们也不考虑解码现在来自字节数组):
from datetime import datetime, timedelta
base, n = datetime(2000, 1, 1, 1, 2, 3, 420001), 1000
datelist = [(base + timedelta(days=i)).isoformat(' ') for i in range(n)]
# datelist
# ['2000-01-01 01:02:03.420001'
# ...
# '2002-09-26 01:02:03.420001']
从字符串到日期时间对象
让我们使用不同的方法定义一些将字符串解析为 datetime
的函数:
import re
import numpy as np
def strp_isostr(l):
return list(map(datetime.fromisoformat, l))
def isostr_to_nparr(l):
return np.array(l, dtype=np.datetime64)
def split_isostr(l):
def splitter(s):
tmp = s.split(' ')
tmp = tmp[0].split('-') + [tmp[1]]
tmp = tmp[:3] + tmp[3].split(':')
tmp = tmp[:5] + tmp[5].split('.')
return datetime(*map(int, tmp))
return list(map(splitter, l))
def resplit_isostr(l):
# return list(map(lambda s: datetime(*map(int, re.split('T|-|\:|\.', s))), l))
return [datetime(*map(int, re.split('\ |-|\:|\.', s))) for s in l]
def full_stptime(l):
# return list(map(lambda s: datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f'), l))
return [datetime.strptime(s, '%Y-%m-%d %H:%M:%S.%f') for s in l]
如果我 运行 %timeit
在 IPython 控制台中使用我机器上的这些功能,我得到
%timeit strp_isostr(datelist)
98.2 µs ± 766 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit isostr_to_nparr(datelist)
1.49 ms ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit split_isostr(datelist)
3.02 ms ± 236 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit resplit_isostr(datelist)
3.8 ms ± 256 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit full_stptime(datelist)
16.7 ms ± 780 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
因此我们可以得出结论,内置 datetime.fromisoformat
是目前为止最快的选项 对于 1000 个元素的输入。但是,这假设您希望使用 list
。无论如何,如果您需要 np.array
或 datetime64
,那么直接使用它似乎是最好的选择。
第三方选项:ciso8601
如果您能够安装额外的软件包,ciso8601
值得一看:
import ciso8601
def ciso(l):
return list(map(ciso8601.parse_datetime, l))
%timeit ciso(datelist)
138 µs ± 1.83 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
从日期时间对象到纪元以来的秒数
查看从 datetime
对象到 POSIX 时间戳的转换,使用最明显的 datetime.timestamp
方法似乎是最有效的:
import time
def dt_ts(l):
return list(map(datetime.timestamp, l))
def timetup(l):
return list(map(time.mktime, map(datetime.timetuple, l)))
%timeit dt_ts(strp_isostr(datelist))
572 µs ± 4.57 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit timetup(strp_isostr(datelist))
1.44 ms ± 15.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)