Почему панды.в datetime медленно для нестандартного формата времени, такого как "2014/12/31"
у меня есть .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 в datetime с помощью pd.to_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
Итак, как я могу улучшить производительность pd.to_datetime
при чтении даты из файла CSV?
3 ответов
это потому, что панды падает обратно в 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
часто я не могу указать стандартный формат даты раньше времени, потому что я просто не знаю, как каждый клиент подберет представить его. Даты непредсказуемо отформатированы и часто отсутствуют.
В этих случаях, вместо того, чтобы использовать 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
в этом случае время выполнения сокращается наполовину, но YMMV.
на этот вопрос уже был дан достаточный ответ, но я хотел добавить результаты некоторых тестов, которые я запускал, чтобы оптимизировать свой собственный код.
я получал этот формат из API: "Wed Feb 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)
%timeit format_datetime_2(dt_series_sample)
%timeit format_datetime_3(dt_series_sample)
%timeit format_datetime_baseline(dt_series_sample)
1 loop, best of 3: 1.56 s per loop
1 loop, best of 3: 2.09 s per loop
1 loop, best of 3: 1.72 s per loop
1 loop, best of 3: 1min 9s per loop
первые результаты теста в поразительном сокращении времени выполнения 97,7%!
несколько удивительно, но похоже, что даже "соответствующее представление" занимает больше времени, вероятно, потому, что оно полу-неявное.
вывод: чем более явным вы являетесь, тем быстрее он будет работать.