熊猫:缓慢的日期转换

时间:2015-04-26 19:40:27

标签: python optimization pandas

我正在阅读一个巨大的CSV,其中的日期字段格式为YYYYMMDD,我在阅读时使用以下lambda进行转换:

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

这个功能很慢。

有任何改善建议吗?

8 个答案:

答案 0 :(得分:58)

尝试使用此函数解析日期:

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

答案 1 :(得分:6)

很棒的建议@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,需要 8分8秒

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.

答案 2 :(得分:5)

使用缓存简化日期解析

读取所有数据然后转换它总是比读取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相同的优点,只需解析每个字符串一次,额外增加的好处就是不必读取所有数据,然后解析它。节省您的记忆和处理时间。

答案 3 :(得分:2)

试用标准库:

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

然而,我真的不知道这是否比熊猫快得多。

由于你的格式很简单,那么

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

编辑您说您需要处理无效数据。

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的东西来为你提供更好的数据 - 这么多格式不依赖于字符串解析。

答案 4 :(得分:2)

pandas version 0.25起,函数pandas.read_csv接受一个cache_dates=boolean(默认为True)关键字参数。因此,无需像接受的答案那样编写自己的缓存功能。

答案 5 :(得分:1)

无需指定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 :(得分:1)

如果你的日期时间有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 :(得分:0)

我有一个约有15万行的csv。尝试了这篇文章中的几乎所有建议之后,我发现可以快25%:

  1. 使用Python3.7本机csv.reader逐行读取文件
  2. 使用float()
  3. 转换所有4个数字列
  4. 使用datetime.datetime.fromisoformat()
  5. 解析日期列

注意:

  1. 最终将列表转换为DataFrame(!)**

这让我感到困惑,怎么会比本地大熊猫pd.read_csv(...)...更快呢?