【问题标题】:How do I efficiently replace the time portion of datetime values in a pandas column?如何有效地替换 pandas 列中日期时间值的时间部分?
【发布时间】:2021-05-05 22:04:20
【问题描述】:

我有一个包含许多日期时间值的数据框。我想将这些时间部分更改为对数据框中所有观察值都相同的特定时间,但保留日期时间的日期部分。

我可以使用datetime 包和.replace() 函数通过单个日期时间完成此操作:

import datetime

dt_test=datetime.datetime(2020, 5, 17,21,30,15)
print(dt_test)

dt_test=dt_test.replace(hour=6,minute=0,second=0)

print(dt_test)

返回:

2020-05-17 21:30:15
2020-05-17 06:00:00

我觉得我应该可以使用 .dt 运算符将其应用于整个列,但是当我尝试这样做时收到错误。

import pandas as pd
dates=[
    datetime.datetime(2018,1,1,4,30,15),
    datetime.datetime(2018,1,2,4,30,15),
    datetime.datetime(2018,1,3,4,30,15),
    datetime.datetime(2018,1,3,6,0,0),
    datetime.datetime(2018,1,3,12,30,15),
    datetime.datetime(2018,1,1,4,30,15),
    datetime.datetime(2018,1,2,4,30,15),
    datetime.datetime(2018,1,4,4,30,15),
    datetime.datetime(2018,1,2,12,30,15),
    datetime.datetime(2018,1,4,12,30,15),
]
ids=list(range(len(dates)))

df=pd.DataFrame(zip(ids,dates),columns=['id','date_time'])
df

返回:

    id  date_time
0   0   2018-01-01 04:30:15
1   1   2018-01-02 04:30:15
2   2   2018-01-03 04:30:15
3   3   2018-01-03 06:00:00
4   4   2018-01-03 12:30:15
5   5   2018-01-01 04:30:15
6   6   2018-01-02 04:30:15
7   7   2018-01-04 04:30:15
8   8   2018-01-02 12:30:15
9   9   2018-01-04 12:30:15

“date_time”列是 dtype: datetime64[ns],根据需要(请参阅this 问题)。

然后我尝试将 pandas 的 .replace() 函数与 .dt 运算符一起使用:

df['date_time'].dt.replace(hour=6,minute=0,second=0)
df

但我收到以下错误(使用 jupyter,如果这很重要):

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-13-65a7bb8c3365> in <module>
----> 1 df['date_time'].dt.replace(hour=6,minute=0,second=0)
      2 df

AttributeError: 'DatetimeProperties' object has no attribute 'replace'

如何有效地替换 pandas 列中 datetime 变量的时间部分?

注意:我发现 this 问题可以将所有日期的时间设置为午夜,但时间对我来说很重要,不太可能是午夜。

【问题讨论】:

  • @MrFuppes 即使我不接受它(我有两个可以接受的答案),我确实认为您的答案增加了价值并展示了另一种做事方式(两种使用 pd.Timedelta() 的另一种方式并使用 floor() 函数。请随意转发它 - 我至少从中学到了。如果您也想包含这些结果,显示基准测试结果也会增加额外的价值。

标签: python pandas dataframe datetime


【解决方案1】:

除了使用normalize()as @QuangHoang 在他的回答中显示的那样,您还可以floor 当天的时间戳(即还给您提供 H:M:S 全部设置为零的日期),并添加一个时间增量:

df['date_time'].dt.floor('d') + pd.Timedelta(hours=6)

另一种选择是通过应用lambda 来使用replace

df['date_time'] = df['date_time'].apply(lambda t: t.replace(hour=6,minute=0,second=0))

在性能方面,迭代 apply 对于较大的系列大小要慢得多:

有趣的是,在此基准中,对于尺寸 > 10k 的元素,今天的地板比规范化方法略有优势。而且我认为floor 更具可读性(我个人的喜好),因为没有一般定义normalize 的含义。


基准测试:Python 3.8.7 x86-64、pandas 1.2.1。代码(另见simple benchmark):

import pandas as pd
from simple_benchmark import benchmark

timeseries = pd.Series([
    pd.Timestamp(2018,1,1,4,30,15),
    pd.Timestamp(2018,1,2,4,30,15),
    pd.Timestamp(2018,1,3,4,30,15),
    pd.Timestamp(2018,1,3,6,0,0),
    pd.Timestamp(2018,1,3,12,30,15),
    pd.Timestamp(2018,1,1,4,30,15),
    pd.Timestamp(2018,1,2,4,30,15),
    pd.Timestamp(2018,1,4,4,30,15),
    pd.Timestamp(2018,1,2,12,30,15),
    pd.Timestamp(2018,1,4,12,30,15),
])

add = pd.Timedelta(hours=6)

def floor_day(s):
    return s.dt.floor('d') + add

def normalize_day(s):
    return s.dt.normalize() + add

def apply_replace(s):
    return s.apply(lambda t: t.replace(hour=6,minute=0,second=0))

funcs = [floor_day, normalize_day, apply_replace]
arguments = {i*timeseries.size: timeseries.repeat(i) for i in [1, 10, 100, 1000, 10000, 100000]}
argument_name = 'series_size'
b = benchmark(funcs, arguments, argument_name)
b.plot()

【讨论】:

  • 这个也不错。我对你的陈述感到惊讶,并认为.apply() 不会是要走的路。但你是对的,至少对于这个玩具例子。也就是说,当我增加数据框大小时(将*1000 添加到dates 的定义中),floor 方法是最快的,紧随其后的是normalize.apply() 方法对我来说慢了大约 90 倍。
  • @amquack:是的,我没有检查大型系列。交叉检查您自己的要求总是很好。
  • apply 未矢量化,因此通常应避免使用。在小数据上,性能差别不大,有时甚至支持apply,因为矢量化开销(内存分配、线程管理等),但对于较大的数据,差异应该是显着的@amquack 指出.
【解决方案2】:

对于hour=0, minute=0, second=0,Pandas 有一个内置的:

df['date_time'].dt.normalize()

然后你就可以换档了:

df['date_time'].dt.normalize() + pd.Timedelta('01:02:03')

【讨论】:

  • 谢谢,但我注意到在我的问题结束时 - 我发现了这一点,但时间对我的问题很重要。有没有办法使用它,然后说将 X 小时/分钟/秒添加到标准化时间?
猜你喜欢
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 2020-10-22
  • 2018-07-15
  • 1970-01-01
  • 2016-05-11
  • 2018-09-16
  • 1970-01-01
相关资源
最近更新 更多