为什么是非标准时间格式pandas.to_datetime慢如“2014年12月31日”

问题描述 投票:32回答:3

我有这样的格式.csv文件

timestmp, p
2014/12/31 00:31:01:9200, 0.7
2014/12/31 00:31:12:1700, 1.9
...

当通过pd.read_csv读取和转换时间STR使用pd.to_datetime为datetime,性能急剧下降。这里是一个小例子。

import re
import pandas as pd

d = '2014-12-12 01:02:03.0030'
c = re.sub('-', '/', d)

%timeit pd.to_datetime(d)
%timeit pd.to_datetime(c)
%timeit pd.to_datetime(c, format="%Y/%m/%d %H:%M:%S.%f")

而表现为:

10000 loops, best of 3: 62.4 µs per loop
10000 loops, best of 3: 181 µs per loop
10000 loops, best of 3: 82.9 µs per loop

所以,从CSV文件中读取日期时,我怎么能提高pd.to_datetime的表现?

python csv pandas python-datetime string-to-datetime
3个回答
43
投票

这是因为大熊猫回落到dateutil.parser.parse解析字符串时,它具有非默认格式或在未提供format字符串(这是更灵活,也更慢)。

正如你所如上图所示,可以提高通过提供format字符串to_datetime性能。或者另一种选择是使用infer_datetime_format=True


显然,当有微秒infer_datetime_format无法推断。对于没有这些例子,你可以看到一个大提速:

In [28]: d = '2014-12-24 01:02:03'

In [29]: c = re.sub('-', '/', d)

In [30]: s_c = pd.Series([c]*10000)

In [31]: %timeit pd.to_datetime(s_c)
1 loops, best of 3: 1.14 s per loop

In [32]: %timeit pd.to_datetime(s_c, infer_datetime_format=True)
10 loops, best of 3: 105 ms per loop

In [33]: %timeit pd.to_datetime(s_c, format="%Y/%m/%d %H:%M:%S")
10 loops, best of 3: 99.5 ms per loop

4
投票

这个问题已经充分回答,但我想在某些测试中,我正在运行,以优化自己的代码的结果添加。

我正在从API格式为:“星期三2月08日17时58分56秒+0000 2017年”。

使用默认pd.to_datetime(SERIES)与隐式转换,它正在采取了一个多小时来处理大约20万行(这取决于我有多少可用内存了)。

这就是说,我测试了三种不同的转换:

# explicit conversion of essential information only -- parse dt str: concat
def format_datetime_1(dt_series):

    def get_split_date(strdt):
        split_date = strdt.split()
        str_date = split_date[1] + ' ' + split_date[2] + ' ' + split_date[5] + ' ' + split_date[3]
        return str_date

    dt_series = pd.to_datetime(dt_series.apply(lambda x: get_split_date(x)), format = '%b %d %Y %H:%M:%S')

    return dt_series

# explicit conversion of what datetime considers "essential date representation" -- parse dt str: del then join
def format_datetime_2(dt_series):

    def get_split_date(strdt):
        split_date = strdt.split()
        del split_date[4]
        str_date = ' '.join(str(s) for s in split_date)
        return str_date

    dt_series = pd.to_datetime(dt_series.apply(lambda x: get_split_date(x)), format = '%c')

    return dt_series

# explicit conversion of what datetime considers "essential date representation" -- parse dt str: concat
def format_datetime_3(dt_series):

    def get_split_date(strdt):
        split_date = strdt.split()
        str_date = split_date[0] + ' ' + split_date[1] + ' ' + split_date[2] + ' ' + split_date[3] + ' ' + split_date[5]
        return str_date

    dt_series = pd.to_datetime(dt_series.apply(lambda x: get_split_date(x)), format = '%c')

    return dt_series

# implicit conversion
def format_datetime_baseline(dt_series):

    return pd.to_datetime(dt_series)

这是结果:

# sample of 250k rows
dt_series_sample = df['created_at'][:250000]

%timeit format_datetime_1(dt_series_sample)        # best of 3: 1.56 s per loop
%timeit format_datetime_2(dt_series_sample)        # best of 3: 2.09 s per loop
%timeit format_datetime_3(dt_series_sample)        # best of 3: 1.72 s per loop
%timeit format_datetime_baseline(dt_series_sample) # best of 3: 1min 9s per loop

第一个测试结果令人印象深刻的97.7%,减少运行时间!

出人意料的是,它看起来像连“适当名额的代表”需要更长的时间,可能是因为它是半隐式。

结论:更明确的你,速度就越快运行。


3
投票

通常,我不能提前指定标准的日期格式,因为我根本不知道每一个客户将如何选择提交。该日期不可预测格式化并经常失踪。

在这些情况下,而不是使用pd.to_datetime,我发现它更有效地写我自己的包装,以dateutil.parser.parse

import pandas as pd
from dateutil.parser import parse
import numpy as np

def parseDateStr(s):
    if s != '':
        try:
            return np.datetime64(parse(s))
        except ValueError:
            return np.datetime64('NaT')
    else: return np.datetime64('NaT')             

# Example data:
someSeries=pd.Series(  ['NotADate','','1-APR-16']*10000 )

# Compare times:
%timeit pd.to_datetime(someSeries, errors='coerce') #1 loop, best of 3: 1.78 s per loop
%timeit someSeries.apply(parseDateStr)              #1 loop, best of 3: 904 ms per loop

# The approaches return identical results:
someSeries.apply(parseDateStr).equals(pd.to_datetime(someSeries, errors='coerce')) # True

在这种情况下,运行时间减少了一半,但情况因人而异。

© www.soinside.com 2019 - 2024. All rights reserved.