Pandas: медленное преобразование даты

Я читаю огромный CSV с полем даты в формате YYYYMMDD, и я использую следующую лямбду для ее преобразования при чтении:

import pandas as pd

df = pd.read_csv(filen,
                 index_col=None,
                 header=None,
                 parse_dates=[0],
                 date_parser=lambda t:pd.to_datetime(str(t),
                                            format='%Y%m%d', coerce=True))

Эта функция работает очень медленно.

Любое предложение по его улучшению?

Ответ 1

Попробуйте использовать эту функцию для синтаксического анализа дат:

def lookup(s):
    """
    This is an extremely fast approach to datetime parsing.
    For large data, the same dates are often repeated. Rather than
    re-parse these, we store all unique dates, parse them, and
    use a lookup to convert all dates.
    """
    dates = {date:pd.to_datetime(date) for date in s.unique()}
    return s.map(dates)

Используйте его как:

df['date-column'] = lookup(df['date-column'])

Ориентиры:

$ python date-parse.py
to_datetime: 5799 ms
dateutil:    5162 ms
strptime:    1651 ms
manual:       242 ms
lookup:        32 ms

Источник: https://github.com/sanand0/benchmarks/tree/master/date-parse

Ответ 2

Отличное предложение @EdChum! Как предлагает @EdChum, использование infer_datetime_format=True может быть значительно быстрее. Ниже приведен мой пример.

У меня есть файл данных о температуре из журнала датчиков, который выглядит следующим образом:

RecNum,Date,LocationID,Unused
1,11/7/2013 20:53:01,13.60,"117","1",
2,11/7/2013 21:08:01,13.60,"117","1",
3,11/7/2013 21:23:01,13.60,"117","1",
4,11/7/2013 21:38:01,13.60,"117","1",
...

Мой код считывает csv и анализирует дату (parse_dates=['Date']). С infer_datetime_format=False требуется 8min 8sec:

Tue Jan 24 12:18:27 2017 - Loading the Temperature data file.
Tue Jan 24 12:18:27 2017 - Temperature file is 88.172 MB.
Tue Jan 24 12:18:27 2017 - Loading into memory. Please be patient.
Tue Jan 24 12:26:35 2017 - Success: loaded 2,169,903 records.

С infer_datetime_format=True требуется 13 сек:

Tue Jan 24 13:19:58 2017 - Loading the Temperature data file.
Tue Jan 24 13:19:58 2017 - Temperature file is 88.172 MB.
Tue Jan 24 13:19:58 2017 - Loading into memory. Please be patient.
Tue Jan 24 13:20:11 2017 - Success: loaded 2,169,903 records.

Ответ 3

Оптимизация даты с кешированием

Чтение всех данных, а затем их преобразование всегда будет медленнее, чем конвертирование при чтении CSV. Поскольку вам не нужно будет перебирать все данные дважды, если вы сделаете это сразу. Вам также не нужно хранить его в виде строк в памяти.

Мы можем определить наш собственный синтаксический анализатор даты, который использует кеш для дат, которые он уже видел.

import pandas as pd

cache = {}

def cached_date_parser(s):
    if s in cache:
        return cache[s]
    dt = pd.to_datetime(s, format='%Y%m%d', coerce=True)
    cache[s] = dt
    return dt

df = pd.read_csv(filen,
                 index_col=None,
                 header=None,
                 parse_dates=[0],
                 date_parser=cached_date_parser)

Имеет те же преимущества, что и @fixxxer s, только разобрав каждую строку, добавив дополнительный бонус к тому, что вам не нужно читать все данные, а THEN разобрать его. Сохранение памяти и времени обработки.

Ответ 4

Попробуйте стандартную библиотеку:

import datetime
parser = lambda t: datetime.datetime.strptime(str(t), "%Y%m%d")

Тем не менее, я не знаю, было ли это намного быстрее, чем pandas.

Поскольку ваш формат настолько прост, что о

def parse(t):
     string_ = str(t)
     return datetime.date(int(string_[:4]), int(string[4:6]), int(string[6:]))

EDIT, вы говорите, что вам нужно позаботиться о недействительных данных.

def parse(t):
     string_ = str(t)
     try:
         return datetime.date(int(string_[:4]), int(string[4:6]), int(string[6:]))
     except:
         return default_datetime #you should define that somewhere else

В целом, я немного не согласен с обоснованностью вашей проблемы:

  • вам нужно быть быстрым, но все же вы получаете свои данные из CSV
  • вам нужно быть быстрым, но все же нужно иметь дело с недопустимыми данными.

Такое противоречие; мой личный подход здесь предполагает, что ваш "огромный" CSV нужно просто привести в более эффективный формат один раз, и вам либо не нужно заботиться о скорости этого процесса конверсии (потому что это происходит только один раз), либо вам следует принести все, что дает CSV, чтобы дать вам лучшие данные - там так много форматов, которые не полагаются на синтаксический анализ строк.

Ответ 5

Нет необходимости указывать date_parser, pandas способен анализировать это без каких-либо проблем, плюс это будет намного быстрее:

In [21]:

import io
import pandas as pd
t="""date,val
20120608,12321
20130608,12321
20140308,12321"""
df = pd.read_csv(io.StringIO(t), parse_dates=[0])
df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 3 entries, 0 to 2
Data columns (total 2 columns):
date    3 non-null datetime64[ns]
val     3 non-null int64
dtypes: datetime64[ns](1), int64(1)
memory usage: 72.0 bytes
In [22]:

df
Out[22]:
        date    val
0 2012-06-08  12321
1 2013-06-08  12321
2 2014-03-08  12321

Ответ 6

Если у вашей даты и времени есть метка времени UTC, и вам нужна только ее часть. Преобразуйте его в строку, нарежьте то, что вам нужно, и затем примените приведенное ниже для более быстрого доступа.

created_at
2018-01-31 15:15:08 UTC
2018-01-31 15:16:02 UTC
2018-01-31 15:27:10 UTC
2018-02-01 07:05:55 UTC
2018-02-01 08:50:14 UTC

df["date"]=  df["created_at"].apply(lambda x: str(x)[:10])


df["date"] = pd.to_datetime(df["date"])

Ответ 7

У меня есть CSV с ~ 150 тыс. Строк. Перепробовав почти все предложения в этом посте, я обнаружил, что на 25% быстрее:

  1. читать файл строка за строкой, используя Python3.7 нативный csv.reader
  2. преобразовать все 4 числовых столбца, используя float() и
  3. проанализировать столбец даты с помощью datetime.datetime.fromisoformat()

и вот:

  1. наконец, преобразовать список в DataFrame (!) **

Это сбивает с толку меня, как это может быть быстрее, чем родные панды pd.read_csv (...)... кто-то может объяснить?