首页 > 解决方案 > 更快的 datetime.strptime

问题描述

试图unixtimestamp从数百万个bytes对象中获取

使用这个

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)。

那么有没有办法从输入而不是输入中获取unixtimestamp ?bytesstr

__更新:__

我发现瓶颈是datetime.datetime.strptime(..),所以我切换到np.datetime64见下文

__UPDATE 2:__ 检查下面接受的答案以获得不同方法的良好性能基准。

标签: pythondatetimebytepython-datetime

解决方案


我们首先假设您在 a 中有 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)

因此我们可以得出结论,对于 1000 个元素的输入,内置datetime.fromisoformat是迄今为止最快的选项。但是,这假设您想要使用 a list。万一你需要一个np.arraydatetime64直接去那个似乎是最好的选择。


第三方选项: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)

推荐阅读