在datetime、Timestamp和datetime64之间转换

461

如何将 numpy.datetime64 对象转换为 datetime.datetime (或 Timestamp)?

在以下代码中,我创建一个datetime,timestamp和datetime64对象。

import datetime
import numpy as np
import pandas as pd
dt = datetime.datetime(2012, 5, 1)
# A strange way to extract a Timestamp object, there's surely a better way?
ts = pd.DatetimeIndex([dt])[0]
dt64 = np.datetime64(dt)

In [7]: dt
Out[7]: datetime.datetime(2012, 5, 1, 0, 0)

In [8]: ts
Out[8]: <Timestamp: 2012-05-01 00:00:00>

In [9]: dt64
Out[9]: numpy.datetime64('2012-05-01T01:00:00.000000+0100')

注意:从时间戳中获取日期时间很容易:

In [10]: ts.to_datetime()
Out[10]: datetime.datetime(2012, 5, 1, 0, 0)

但是我们如何从 numpy.datetime64 (dt64) 中提取 datetimeTimestamp?

.

更新:我的数据集中有一个有点棘手的例子(可能是激励性的例子),它似乎是:

dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100')

应该是datetime.datetime(2002, 6, 28, 1, 0),而不是一个长整型数值(1025222400000000000L)!


3
你应该接受 @Wes McKinney 的答案,它更简短,并且应该适用于最近的 numpypandas 版本。 - jfs
@J.F.Sebastian 嗯,这是否意味着答案是“不要从np.datetime转换到datetime”...只需使用pd.Timestamp(因为它是datetime的子类),或者如果您真的必须使用pd.Timestamp(dt64).to_datetime()。我仍然有点不满意,但肯定Wes'对我的旧问题不太具体(因此更适合世界)!再次感谢您抽出时间来回答它。 :) - Andy Hayden
你的问题中提到了“或者Timestamp”,而Timestamp本身就是datetime(它的一个子类):) - jfs
7
如果您是在2017年或之后来到这个问题,可以查看我下面的答案,其中详细介绍了datetime、datetime64和Timestamps的教程:https://dev59.com/I2Yr5IYBdhLWcg3waJdT#46921593 - Ted Petrou
对于Numpy -> datetime,截至2020年,字符串转换是最优雅的选项。 - tejasvi88
14个回答

327
您可以使用“pd.Timestamp constructor”进行操作。以下的图表对此及相关问题可能很有用。

不同时间表示之间的转换


418
仅仅看这个图表,我就知道所有时间相关的东西在根本上存在问题。 - demented hedgehog
19
如果使用pd.to_datetime给定的是毫秒或纳秒数,它会产生一个TimeStamp对象,但如果给定的是datetime.datetime对象或者np.datetime64对象,它会分别产生一个datetime.datetime对象或者np.datetime64对象,这很令人困惑。为什么有人认为这是合理的呢? - Mr.WorshipMe
6
pd.Timestamp() 将 np.datetime 转换为 pd.Timestamp。 - zthomas.nc
30
@Mr.WorshipMe 这张图需要更新。pd.to_datetime 将所有内容转换为 pd.Timestamp。一个 pd.Timestamp 对象具有 to_pydatetime 方法,可将其还原为 datetime.datetime 对象,并具有 to_datetime64 方法,可将其转换为 np.datetime64 - Ted Petrou
8
我如何获得这张图片更高的分辨率? - user3226167
显示剩余9条评论

223

欢迎来到地狱。

您可以将datetime64对象直接传递给pandas.Timestamp

In [16]: Timestamp(numpy.datetime64('2012-05-01T01:00:00.000000'))
Out[16]: <Timestamp: 2012-05-01 01:00:00>

我注意到在NumPy 1.6.1中这个功能无法正常工作:

numpy.datetime64('2012-05-01T01:00:00.000000+0100')

此外,可以使用 pandas.to_datetime (这是从开发版本中获取的,尚未检查v0.9.1):

Also, pandas.to_datetime 可以使用(这是来自开发版本,未检查v0.9.1):

In [24]: pandas.to_datetime('2012-05-01T01:00:00.000000+0100')
Out[24]: datetime.datetime(2012, 5, 1, 1, 0, tzinfo=tzoffset(None, 3600))

5
你应该提到 issubclass(pd.Timestamp, datetime)True。而且 Timestamp 类本身具有 to_datetime() 方法。 - jfs
11
在pandas中,pd.to_datetime('2012-05-01T01:00:00.000000+0100') 的返回值至少在版本为0.17.1时是 Timestamp('2012-05-01 00:00:00') - Anton Protopopov
1
将datetime64列转换为Timestamp的简单方法是my_datetime64_column.apply(lambda x: pd.Timestamp(x)) - John Mark

190
numpy-1.8 中表示UTC时间的 numpy.datetime64 转换为 datetime 对象的方法:
>>> from datetime import datetime
>>> import numpy as np
>>> dt = datetime.utcnow()
>>> dt
datetime.datetime(2012, 12, 4, 19, 51, 25, 362455)
>>> dt64 = np.datetime64(dt)
>>> ts = (dt64 - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
>>> ts
1354650685.3624549
>>> datetime.utcfromtimestamp(ts)
datetime.datetime(2012, 12, 4, 19, 51, 25, 362455)
>>> np.__version__
'1.8.0.dev-7b75899'

上面的示例假设一个简单的datetime对象被解释为UTC时间的np.datetime64
datetime转换为np.datetime64并返回(numpy-1.6):
>>> np.datetime64(datetime.utcnow()).astype(datetime)
datetime.datetime(2012, 12, 4, 13, 34, 52, 827542)

它既适用于单个的np.datetime64对象,也适用于一组np.datetime64的numpy数组。

np.datetime64看作与np.int8np.int16等相同,并应用相同的方法来在Python对象(如intdatetime)和相应的numpy对象之间进行转换。

你的“nasty example”可以正常工作:

>>> from datetime import datetime
>>> import numpy 
>>> numpy.datetime64('2002-06-28T01:00:00.000000000+0100').astype(datetime)
datetime.datetime(2002, 6, 28, 0, 0)
>>> numpy.__version__
'1.6.2' # current version available via pip install numpy

我可以在安装了 numpy-1.8.0 的环境中重现long值。
pip install git+https://github.com/numpy/numpy.git#egg=numpy-dev

同样的例子:

>>> from datetime import datetime
>>> import numpy
>>> numpy.datetime64('2002-06-28T01:00:00.000000000+0100').astype(datetime)
1025222400000000000L
>>> numpy.__version__
'1.8.0.dev-7b75899'

这里返回的是long类型,因为对于numpy.datetime64类型而言,.astype(datetime)等同于.astype(object),后者会返回Python整型(long)在numpy-1.8中。

若要得到datetime对象,您可以:

>>> dt64.dtype
dtype('<M8[ns]')
>>> ns = 1e-9 # number of seconds in a nanosecond
>>> datetime.utcfromtimestamp(dt64.astype(int) * ns)
datetime.datetime(2002, 6, 28, 0, 0)

为了获得直接使用秒的datetime64
>>> dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100', 's')
>>> dt64.dtype
dtype('<M8[s]')
>>> datetime.utcfromtimestamp(dt64.astype(int))
datetime.datetime(2002, 6, 28, 0, 0)

Numpy文档指出日期时间API目前还处于试验阶段,并可能在未来的Numpy版本中进行更改。


3
很抱歉,这似乎并不总是有效:例如,dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100')会返回一个很长的值 (1025222400000000000L)。 - Andy Hayden
@JFSebastian type(dt64)numpy.datetime64 而且 dt64.astype(datetime) 是相同的长整数... :s - Andy Hayden
版本1.8.0(在Python 2.7.3中),如果它能正常工作,那就意味着这是我系统上的一个错误! - Andy Hayden
这些示例对我来说都无效。如果我尝试datetime.datetime.utcfromtimestamp,我会看到两个错误:(1)当我尝试传入未更改的np.datetime64时,出现"TypeError: don't know how to convert scalar number to float";然后(2)当我首先使用.astype(int)时,出现"ValueError: year is out of range"。测试日期为2012年:xx = numpy.datetime64(datetime.date(2012,1,31)),因此年份完全有效。我使用的是NumPy 1.6.1版本。 - ely
@EMS:运行此代码(如果成功,您将看不到任何内容)。如果出现任何错误,请提供可运行的代码链接。 - jfs
显示剩余2条评论

162

我认为有必要更加集中的努力来解释Python的datetime模块、numpy的datetime64/timedelta64和pandas的Timestamp/Timedelta对象之间的关系。

Python的datetime标准库

Python的datetime标准库有四个主要对象:

  • time - 只包含时间,以小时、分钟、秒和微秒表示
  • date - 只包含年、月和日
  • datetime - 时间和日期的所有组成部分
  • timedelta - 最大单位为天的一段时间

创建这四个对象

>>> import datetime
>>> datetime.time(hour=4, minute=3, second=10, microsecond=7199)
datetime.time(4, 3, 10, 7199)

>>> datetime.date(year=2017, month=10, day=24)
datetime.date(2017, 10, 24)

>>> datetime.datetime(year=2017, month=10, day=24, hour=4, minute=3, second=10, microsecond=7199)
datetime.datetime(2017, 10, 24, 4, 3, 10, 7199)

>>> datetime.timedelta(days=3, minutes = 55)
datetime.timedelta(3, 3300)

>>> # add timedelta to datetime
>>> datetime.timedelta(days=3, minutes = 55) + \
    datetime.datetime(year=2017, month=10, day=24, hour=4, minute=3, second=10, microsecond=7199)
datetime.datetime(2017, 10, 27, 4, 58, 10, 7199)

NumPy的datetime64和timedelta64对象

NumPy没有单独的日期和时间对象,只有一个datetime64对象来表示某个时间点。datetime模块的datetime对象精确到微秒(一百万分之一秒)。NumPy的datetime64对象允许您将其精度设置从小时到attoseconds(10 ^ -18)。它的构造函数更灵活,可以接受各种输入。

构建NumPy的datetime64和timedelta64对象

传递一个带有字符串单位的整数。在此处查看所有单位。 它会在UNIX纪元:1970年1月1日之后转换为这么多个单位。

>>> np.datetime64(5, 'ns') 
numpy.datetime64('1970-01-01T00:00:00.000000005')

>>> np.datetime64(1508887504, 's')
numpy.datetime64('2017-10-24T23:25:04')

只要字符串符合ISO 8601格式,您也可以使用它们。

>>> np.datetime64('2017-10-24')
numpy.datetime64('2017-10-24')

时间增量只有一个单位

>>> np.timedelta64(5, 'D') # 5 days
>>> np.timedelta64(10, 'h') 10 hours

也可以通过减去两个datetime64对象来创建它们

>>> np.datetime64('2017-10-24T05:30:45.67') - np.datetime64('2017-10-22T12:35:40.123')
numpy.timedelta64(147305547,'ms')

Pandas的时间戳和时间增量在NumPy的基础上构建了更多功能

Pandas的时间戳(Timestamp)类似于datetime,但具有更强的功能。您可以使用pd.Timestamppd.to_datetime构造它们。

>>> pd.Timestamp(1239.1238934) #defaults to nanoseconds
Timestamp('1970-01-01 00:00:00.000001239')

>>> pd.Timestamp(1239.1238934, unit='D') # change units
Timestamp('1973-05-24 02:58:24.355200')

>>> pd.Timestamp('2017-10-24 05') # partial strings work
Timestamp('2017-10-24 05:00:00')

pd.to_datetime 的工作方式非常类似(有更多选项),可以将字符串列表转换为时间戳。

>>> pd.to_datetime('2017-10-24 05')
Timestamp('2017-10-24 05:00:00')

>>> pd.to_datetime(['2017-1-1', '2017-1-2'])
DatetimeIndex(['2017-01-01', '2017-01-02'], dtype='datetime64[ns]', freq=None)

将Python datetime转换为datetime64和Timestamp

>>> dt = datetime.datetime(year=2017, month=10, day=24, hour=4, 
                   minute=3, second=10, microsecond=7199)
>>> np.datetime64(dt)
numpy.datetime64('2017-10-24T04:03:10.007199')

>>> pd.Timestamp(dt) # or pd.to_datetime(dt)
Timestamp('2017-10-24 04:03:10.007199')

将numpy的datetime64转换为datetime和Timestamp

>>> dt64 = np.datetime64('2017-10-24 05:34:20.123456')
>>> unix_epoch = np.datetime64(0, 's')
>>> one_second = np.timedelta64(1, 's')
>>> seconds_since_epoch = (dt64 - unix_epoch) / one_second
>>> seconds_since_epoch
1508823260.123456

>>> datetime.datetime.utcfromtimestamp(seconds_since_epoch)
>>> datetime.datetime(2017, 10, 24, 5, 34, 20, 123456)

转换为时间戳

>>> pd.Timestamp(dt64)
Timestamp('2017-10-24 05:34:20.123456')

将时间戳转换为datetime和datetime64

这非常容易,因为Pandas的时间戳功能非常强大。

>>> ts = pd.Timestamp('2017-10-24 04:24:33.654321')

>>> ts.to_pydatetime()   # Python's datetime
datetime.datetime(2017, 10, 24, 4, 24, 33, 654321)

>>> ts.to_datetime64()
numpy.datetime64('2017-10-24T04:24:33.654321000')

11
NumPy 到日期时间转换仍然很困难/不自然,这真的没有更好的方法吗? 这是一个好的答案,我正在考虑接受将其移动到顶层。一旦由计算机深入阅读其他内容。 - Andy Hayden
有什么奇怪的地方吗?Pandas时间戳运作良好且相当简单。 - Ted Petrou
4
Numpy 转换为日期时间。 - Andy Hayden
3
我认为这是我见过的最好的答案。 对于那些来自Excel、VBA、SAS或SQL的人来说,Python似乎很奇怪,因为处理日期/时间不只有“一种方式”。与Python或R中的许多事物一样,似乎必须选择一个喜爱的方法/模块/类并坚持使用它。 - Sean McCarthy
1
是的,非常好的答案。我终于更好地理解了这个问题。显然还有matplotlib.dates,为什么呢? - Santiago
显示剩余3条评论

38
>>> dt64.tolist()
datetime.datetime(2012, 5, 1, 0, 0)

对于 DatetimeIndextolist 方法返回一个包含多个 datetime 对象的列表。对于单个 datetime64 对象,它返回一个包含单个 datetime 对象的列表。


5
如果你知道它是一个标量/0维数组,我更倾向于使用.item(),这更加明确(并且没有人可以开始争论它应该返回一个列表)。 - seberg
3
恐怕这似乎并不总是有效的:例如 dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100'),它会给出一个很长的值(1025222400000000000L)! - Andy Hayden
5
.item()返回的类型(由@seberg建议),.tolist()的结果取决于datetime64使用的单位,例如,D会产生datetime.date()us(微秒)会产生datetime.datetime()ns(纳秒)会产生long。而单位会根据输入值的不同而改变,例如,numpy.datetime64('2012-05-01')使用'D'numpy.datetime64('2012-05-01T00:00:00.000')使用msnumpy.datetime64('2012-05-01T00:00:00.000000000')使用ns。如果您发现这令人困惑,可以提出问题 - jfs
@AndyHayden 你也可以添加一个额外的参数,'us'或'ms',以确保应用相同的格式,从而产生相同的datetime元素在tolist()中。 - N M

16

一种选择是使用str,然后使用to_datetime(或类似函数):

In [11]: str(dt64)
Out[11]: '2012-05-01T01:00:00.000000+0100'

In [12]: pd.to_datetime(str(dt64))
Out[12]: datetime.datetime(2012, 5, 1, 1, 0, tzinfo=tzoffset(None, 3600))

注意:它与dt不相等,因为它已经变成了“带有时区偏移量的”

In [13]: pd.to_datetime(str(dt64)).replace(tzinfo=None)
Out[13]: datetime.datetime(2012, 5, 1, 1, 0)

这看起来不太优雅。

.

更新:这可以处理“令人讨厌的示例”:

In [21]: dt64 = numpy.datetime64('2002-06-28T01:00:00.000000000+0100')

In [22]: pd.to_datetime(str(dt64)).replace(tzinfo=None)
Out[22]: datetime.datetime(2002, 6, 28, 1, 0)

感谢Andy分享这个技巧。由于我在这里讨论的原因,我无法使其正常工作:https://dev59.com/bGEh5IYBdhLWcg3wEwDv - Amelio Vazquez-Reina
@user815423426 这从来不是一个非常健壮的解决方案,我猜你可以传递一个格式到datetime构造函数中以更普遍地工作。虽然不是非常pandastic! - Andy Hayden

16

如果您想将整个pandas序列中的日期时间转换为普通的Python日期时间,您也可以使用.to_pydatetime()

pd.date_range('20110101','20110102',freq='H').to_pydatetime()

> [datetime.datetime(2011, 1, 1, 0, 0) datetime.datetime(2011, 1, 1, 1, 0)
   datetime.datetime(2011, 1, 1, 2, 0) datetime.datetime(2011, 1, 1, 3, 0)
   ....

它还支持时区:

pd.date_range('20110101','20110102',freq='H').tz_localize('UTC').tz_convert('Australia/Sydney').to_pydatetime()

[ datetime.datetime(2011, 1, 1, 11, 0, tzinfo=<DstTzInfo 'Australia/Sydney' EST+11:00:00 DST>)
 datetime.datetime(2011, 1, 1, 12, 0, tzinfo=<DstTzInfo 'Australia/Sydney' EST+11:00:00 DST>)
....

注意:如果您正在操作Pandas Series,不能在整个Series上调用to_pydatetime()。您需要使用列表推导式或类似的方法在每个单独的datetime64上调用.to_pydatetime()

datetimes = [val.to_pydatetime() for val in df.problem_datetime_column]

10
这篇文章已经发布了4年,但我仍在苦恼这个转换问题——从某种意义上说,这个问题在2017年仍然存在。让我有些震惊的是,numpy文档并没有提供一个简单的转换算法,但这又是另一回事了。
我发现还有另一种方法可以进行转换,只需要使用numpydatetime模块,不需要导入pandas,因为它看起来对于如此简单的转换来说过于冗长。我注意到datetime64.astype(datetime.datetime)将返回一个datetime.datetime对象,如果原始的datetime64毫秒单位,而其他单位则返回整数时间戳。我使用模块xarray从Netcdf文件中进行数据输入/输出,它使用纳秒单位的datetime64进行转换,除非您先将其转换为微秒单位,否则将无法进行转换。以下是示例转换代码:
import numpy as np
import datetime

def convert_datetime64_to_datetime( usert: np.datetime64 )->datetime.datetime:
    t = np.datetime64( usert, 'us').astype(datetime.datetime)
return t

这只是在我的电脑上测试的,使用的是Python 3.6和最新的2017年Anaconda发行版。我仅仅查看了标量转换并未检查基于数组的转换,虽然我猜测它应该没问题。同时我也没有查看numpy datetime64源代码来确定操作是否合理。


1

我已经回到这个答案很多次了,所以我决定快速编写一个小类,将Numpy datetime64值转换为Python datetime值。希望它能帮助其他人。

from datetime import datetime
import pandas as pd

class NumpyConverter(object):
    @classmethod
    def to_datetime(cls, dt64, tzinfo=None):
        """
        Converts a Numpy datetime64 to a Python datetime.
        :param dt64: A Numpy datetime64 variable
        :type dt64: numpy.datetime64
        :param tzinfo: The timezone the date / time value is in
        :type tzinfo: pytz.timezone
        :return: A Python datetime variable
        :rtype: datetime
        """
        ts = pd.to_datetime(dt64)
        if tzinfo is not None:
            return datetime(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, tzinfo=tzinfo)
        return datetime(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second)

我会把这个放在我的工具包里,有预感我会再次需要它。


3
您可以使用 ts.to_pydatetime() 进行转换。 - Ted Petrou

1
import numpy as np
import pandas as pd 

def np64toDate(np64):
    return pd.to_datetime(str(np64)).replace(tzinfo=None).to_datetime()

使用此函数获取Python的本地日期时间对象。

1
我收到了一个错误,说“replace()”得到了一个意外的关键字参数“tzinfo”。 - wlad
你使用的 pandas 版本是哪个?我使用的版本是:0.18.1(pip show pandas)。 - Crystal
same as you. . . - wlad
我不知道为什么,但它对我来说像魔法一样有效。http://pix.toile-libre.org/upload/original/1475645621.png - Crystal

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