为什么 Pandas 拒绝读取 9 个世纪后的日期?

Why Pandas refuse to read a date 9 centuries into the future?

考虑这个例子 df.


import pandas as pd
from io import StringIO
mycsv = StringIO("id,date\n1,11/07/2018\n2,11/07/<b>2918</b>\n3,02/01/2019")
df = pd.read_csv(mycsv)
df
    id  date
0   1   11/07/2018
1   2   11/07/2918
2   3   02/01/2019

很明显那里有错字(2918 而不是 2018),但我还是想将其解析为日期。

所以让我们检查一下df.dtypes

id       int64
date    object
dtype: object

好的,默认情况下它被读取为一个字符串。所以我会明确告诉 read_csv 将该列解析为日期。

df = pd.read_csv(mycsv, parse_dates=["date"])

但是df.dtypes仍然显示日期被读取为字符串(对象数据类型)。

如果我更正错字...


mycsv = StringIO("id,date\n1,11/07/2018\n2,<b>11/07/2018</b>\n3,02/01/2019")

有效

df = pd.read_csv(mycsv, parse_dates=["date"])
df

    id  date
0   1   2018-11-07
1   2   2018-11-07
2   3   2019-02-01

df.dtypes

id               int64
date    datetime64[ns]
dtype: object

很明显,它无法解析这样一个不切实际的日期 (11/07/2918),然后整个列都作为字符串处理。

但为什么它不能正确处理 11/07/2918 日期?以及如何让它正确解析这样的日期?

read_csv 文档说默认情况下它使用 dateutil.parser.parse。当您手动尝试时:

import dateutil
dateutil.parser.parse("13/07/2918") 

它很管用。没有异常,没有错误并生成有效的 datetime 对象:datetime.datetime(2918, 7, 13, 0, 0)

也可以将其转换为 numpy.datetime64 有效

import dateutil
toy = dateutil.parser.parse("13/07/2918")
np.datetime64(toy)

它生成一个有效且正确解析的对象。

numpy.datetime64('2918-07-13T00:00:00.000000')

同样,使用 pandas' strptime 可以正常工作并生成有效的日期时间对象。

pd.datetime.strptime("11/07/2918", "%d/%m/%Y")

现在,尝试使用自定义日期解析器,只是为了确保日期格式正确

mycsv = StringIO("id,date\n1,11/07/2018\n2,11/07/2918\n3,02/01/2019")
df = pd.read_csv(
    mycsv,
    parse_dates=["date"],
    date_parser=lambda x: pd.datetime.strptime(x, "%d/%m/%Y")
)

同样 df["date"].dtypedtype('O')

好的,所以我放弃了说服 read_csv 正确解析日期的尝试。所以我说,让我们把它转换成日期吧。

这一个

df["date"].astype("datetime64")

或这个

pd.to_datetime(df["date"])

抛出和异常

OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 2918-07-11 00:00:00

似乎没有任何效果。

知道为什么会发生这种情况以及如何让它发挥作用吗?

来自文档:

Since pandas represents timestamps in nanosecond resolution, the time span that can 
be represented using a 64-bit integer is limited to approximately 584 years:

In [92]: pd.Timestamp.min
Out[92]: Timestamp('1677-09-21 00:12:43.145225')

In [93]: pd.Timestamp.max
Out[93]: Timestamp('2262-04-11 23:47:16.854775807')

如何表示越界时间: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-oob