使用pandas.to_datetime时仅保留日期部分

399

我使用 pandas.to_datetime 来解析我的数据中的日期。尽管这些日期只有每天的时间,但默认情况下Pandas使用datetime64[ns]来表示日期。 我想知道是否有一种优雅/聪明的方式将这些日期转换为datetime.datedatetime64[D],以便在将数据写入CSV时,日期不会附加上00:00:00。我知道我可以逐个元素手动转换类型:

[dt.to_datetime().date() for dt in df.dates]

但是由于我有很多行,这种方法非常慢,而且有点违背使用 pandas.to_datetime 的初衷。有没有一种方法可以一次转换整个列的dtype?或者,pandas.to_datetime是否支持精度规格,以便我在处理每日数据时可以去掉时间部分?

13个回答

599

自从版本0.15.0以来,现在可以使用{{link2:.dt}}轻松访问仅日期组件:

df['just_date'] = df['dates'].dt.date

上面返回的是datetime.date,所以是object类型。如果你想保持dtype为datetime64,那么你可以直接normalize
df['normalised_date'] = df['dates'].dt.normalize()

这将时间组件设置为午夜,即00:00:00,但显示仅显示日期值。


@edChum 有没有办法以 MM-DD-YYYY 格式格式化日期? - Rrptm
5
只能在类似日期时间的值上使用.dt访问器。 - huang

90

简单解决方案:

df['date_only'] = df['date_time_column'].dt.date

54
只是一个警告,这会将类型更改为对象。因此,您需要使用astype('datetime64')以保持一致性。 - misantroop

40

虽然我已经给EdChum的答案点赞了,这是对于提问者所提问题最为直接的回答,但事实上它并没有真正解决性能问题(因为它仍然依赖于Python的datetime对象,所以对它们进行任何操作都不会向量化——也就是说,会很慢)。

更好的性能替代方案是:

df['dates'].dt.floor('d')

严格说来,它并没有“仅保留日期部分”,而只是将时间设置为00:00:00。但当例如:
  • 在屏幕上打印
  • 保存到CSV文件
  • 使用该列进行groupby
时,它确实可以按照OP所需的方式工作,并且由于操作向量化,效率要高得多。 编辑:事实上,OP可能更喜欢的答案是:“最近版本的pandas如果所有观测值的时间为00:00:00,则不会将其写入CSV”。

不幸的是,to_json 仍会写入完整的 00:00:00 - IanS
@IanS 你的意思是使用“date_format ='iso'”时吗?默认情况下,它只输出自纪元以来的秒数。 - Pietro Battiston
是的,那就是我想表达的意思。 - IanS
这比在长度超过几百个元素的系列上使用dt.normalize()更快。 - C8H10N4O2
1
在一个超过100万行的数据框中,这比我发现的其他选项都要快得多,除了normalize之外,它只慢了一点。此外,保持日期为pandas本地格式意味着可以将其保存到hdf存储中(自pandas 1.1.4以来,datetime.date列无法执行此操作)。 - fantabolous

23

Pandas v0.13+:使用带有date_format参数的to_csv

尽量避免将您的datetime64[ns]系列转换为datetime.date对象的object dtype系列,后者通常是使用pd.Series.dt.date构建的,存储为指针数组,与纯NumPy-based系列相比效率较低。

由于您的关注点是写入CSV时的格式,只需使用to_csvdate_format参数即可。例如:

df.to_csv(filename, date_format='%Y-%m-%d')

请参考Python的strftime指令了解格式规范。


22

这是一种简单的提取日期的方法:

import pandas as pd

d='2015-01-08 22:44:09' 
date=pd.to_datetime(d).date()
print(date)

OP在他们的问题中已经使用了.date()方法,所以这个解决方案并不能回答他们的问题,但我发现它很有用,可以作为使用date()方法的简单示例参考。 - Nic Scozzaro
1
当作为日期解析器的lambda函数使用时,这似乎无法正常工作,即date_parser = lambda col: pd.to_datetime(col, format ="%y%m%d").date()。 - rdmtinez

21
Pandas的DatetimeIndex和Series有一个名为normalize的方法,正好可以满足您的要求。您可以在this answer中了解更多信息。它可以使用ser.dt.normalize()

18
df['Column'] = df['Column'].dt.strftime('%m/%d/%Y')

这将以您所需的格式仅提供日期,而不包括时间。您可以根据需要更改格式'%m/%d/%Y'。它将更改列的数据类型为'object'


如果您只需要日期,而不需要时间,并且希望以 YYYY-MM-DD 格式显示,请使用:

df['Column'] = pd.to_datetime(df['Column']).dt.date

数据类型将会是'object'

对于 'datetime64' 数据类型,请使用:

df['Column'] = pd.to_datetime(df['Column']).dt.normalize()

15

如果有人看到这篇旧帖子,我想给出一个更加实时的答案。

在转换为datetime时添加“utc=False”将删除时区组件,并仅在datetime64 [ns]数据类型中保留日期。

pd.to_datetime(df['Date'], utc=False)

你将能够在Excel中保存它,而不会出现错误“ValueError:Excel不支持带有时区的日期时间,请在写入Excel之前确保日期时间没有时区。”

输入图像描述


1
当你对该列应用任何聚合函数后,它会因某种原因失败。 - RaphX
1
@Katekarin 如果你尝试并查看 df['Date'].values,它仍将保留时间组件。 - Tushar Tiwari

6

转换为datetime64[D]

df.dates.values.astype('M8[D]')

尽管将其分配给DataFrame列将使其恢复为[ns]。

如果你想要实际的datetime.date

dt = pd.DatetimeIndex(df.dates)
dates = np.array([datetime.date(*date_tuple) for date_tuple in zip(dt.year, dt.month, dt.day)])

3
如果您使用astype('M8 [D]')将缺失值转换为原始日期1970-1-1。现在最好使用pandas.to_datetime()。 - Stewbaca
1
注意,对于经常将datetime模块命名为“dt”的人,此答案片段将覆盖该模块!@Dale-Jung,也许可以将该行更改为dt_index之类的内容。 - yeliabsalohcin
我还发现一个问题,即下一次尝试通过df.loc[date]方法添加新行时,索引会恢复为时间戳,这意味着后续的比较将不再起作用。 - yeliabsalohcin

5

我想要能够更改数据框中一组列的类型,并删除时间只保留日期。 round()、floor()、ceil() 都可以实现。

df[date_columns] = df[date_columns].apply(pd.to_datetime)
df[date_columns] = df[date_columns].apply(lambda t: t.dt.floor('d'))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接