在Python中将一列日期时间转换为epoch

45

我目前在Python中遇到一个问题。我有一个Pandas DataFrame,其中一列是一个带有日期字符串的列。

格式为:

"%Y-%m-%d %H:%m:00.000"。例如:"2011-04-24 01:30:00.000"

我需要将整个列转换为整数。我尝试运行此代码,但速度非常慢,并且我有几百万行。

for i in range(calls.shape[0]):
    calls['dateint'][i] = int(time.mktime(time.strptime(calls.DATE[i], "%Y-%m-%d %H:%M:00.000")))

你们知道如何将整个列转换为时间戳吗?

6个回答

53

使用to_datetime将字符串转换为datetime,然后减去1970-1-1的日期时间,并调用dt.total_seconds()

In [2]:
import pandas as pd
import datetime as dt
df = pd.DataFrame({'date':['2011-04-24 01:30:00.000']})
df

Out[2]:
                      date
0  2011-04-24 01:30:00.000

In [3]:
df['date'] = pd.to_datetime(df['date'])
df

Out[3]:
                 date
0 2011-04-24 01:30:00

In [6]:    
(df['date'] - dt.datetime(1970,1,1)).dt.total_seconds()

Out[6]:
0    1303608600
Name: date, dtype: float64
您可以看到,将该值转换回来会产生相同的时间:
In [8]:
pd.to_datetime(1303608600, unit='s')

Out[8]:
Timestamp('2011-04-24 01:30:00')

所以你可以添加新列或覆盖现有列:

In [9]:
df['epoch'] = (df['date'] - dt.datetime(1970,1,1)).dt.total_seconds()
df

Out[9]:
                 date       epoch
0 2011-04-24 01:30:00  1303608600

编辑

根据 @Jeff 提出的建议,采用更好的方法:

In [3]:
df['date'].astype('int64')//1e9

Out[3]:
0    1303608600
Name: date, dtype: float64

In [4]:
%timeit (df['date'] - dt.datetime(1970,1,1)).dt.total_seconds()
%timeit df['date'].astype('int64')//1e9

100 loops, best of 3: 1.72 ms per loop
1000 loops, best of 3: 275 µs per loop

您还可以看到它明显更快


2
请注意,astype('int64')//1e9会将“NaT”(日期时间缺失)转换为-9223372037.0。而total_seconds()则可以正确返回NAN。因此,尽管更麻烦,但使用 total_seconds() 肯定更安全。 - Eudald

24
我知道这是老旧的,但我认为最正确(也是最干净)的方法是下面这个单行代码:
calls['DATE'].apply(lambda x: x.timestamp())

假设 calls['DATE']datetime64[ns] 类型。如果不是,请使用以下方法进行转换:

pd.to_datetime(calls['DATE'], format="%Y-%m-%d %H:%m:00.000")

解释

要获取 pd.Timestamp 的时代值(以秒为单位),请使用:

pd.Timestamp('20200101').timestamp()

这将给您 1577836800.0。如果您想要,您可以将其转换为一个 int。它是浮点数的原因是任何子秒时间都将在小数部分中。

您还可以获取原始的纪元值(以纳秒为单位):

pd.Timestamp('20200101').value

给出1577836800000000000,这是上述日期的纪元时间。 .value 属性是自纪元以来的纳秒数,因此将其除以1e6可得到毫秒数。如果您希望使用第一个调用返回以秒为单位的纪元时间,请除以1e9。


10

Pandas文档中关于处理时间序列数据的内容:

我们先减去时代(1970年1月1日UTC午夜),然后进行“单位”(1毫秒)的整除。

# generate some timestamps
stamps = pd.date_range('2012-10-08 18:15:05', periods=4, freq='D')

# convert it to milliseconds from epoch
(stamps - pd.Timestamp("1970-01-01")) // pd.Timedelta('1ms')

这将给出以毫秒为单位的纪元时间。


2

在s5s的回答上进行补充,我认为代码可以进一步概括以适应缺失数据(例如由pd.NaT表示)。 在Pandas 1.2.4上进行了测试,在Pandas < 1.0上无法工作。

calls['DATE'].apply(lambda x: x.timestamp() if not pd.isna(x) else pd.NA).astype('Int64')

一些注释:

  • pd.isna() 将捕获 pd.NaT(缺失日期时间)

  • lambda 表达式将 pd.NaT 转换为 pd.NA,它将成为缺失数据的新表示方式

  • 最后,lambda 表达式的输出将包含整数和 pd.NA 的混合值,因此我们需要一个 Pandas ExtensionDtype(例如 Int64)来处理它们

示例输出:

0            <NA>
1            <NA>
2            <NA>
3            <NA>
4            <NA>
          ...
865    1619136000
866    1619136000
          ...
Name: DATE, Length: 870, dtype: Int64

0
如果目标只是将日期时间转换为数字,那么您可以将日期时间列视为int64。由于默认的分辨率是纳秒,要转换为秒,int64 的结果必须除以10^9。
df = pd.DataFrame({'date': pd.date_range('2020','2021', 3)})
df['as_int64'] = df['date'].view('int64')
df['seconds_since_epoch'] = df['date'].view('int64') // 10**9

result

另一种方法是,在减去Unix纪元后,将dtype转换为'timedelta64[s]'(注意[s]),以指定您希望以秒为单位的差异,或者'timedelta[ms]'以指定应该以毫秒为单位等。
df['epoch'] = df['date'].sub(pd.Timestamp('1970-01-01')).astype('timedelta64[s]')

0

在撰写这些代码时,您可以使用pandas(已测试版本1.5.2)轻松实现此操作。以下是一个使用填充有表示时间戳的字符串的DataFrame的工作示例。

df = pd.DataFrame(data=["2022-08-01T22:45:12", "2022-08-01T22:46:12", "2022-08-01T22:47:12"], columns=["time"])
df['time'].apply(lambda x: pd.Timestamp(x).timestamp())

请注意,函数timestamp()返回一个浮点型的POSIX时间戳。如果您的时间戳没有毫秒,您可以将结果转换为整数。
df['time'].apply(lambda x: int(pd.Timestamp(x).timestamp()))

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