从 Python(熊猫)中的日期列获取周开始日期(星期一)?

Posted

技术标签:

【中文标题】从 Python(熊猫)中的日期列获取周开始日期(星期一)?【英文标题】:Get week start date (Monday) from a date column in Python (pandas)? 【发布时间】:2015-03-15 08:42:00 【问题描述】:

我已经看过很多关于如何使用日期字符串进行操作的帖子,但我正在为数据框列尝试一些东西,但到目前为止还没有任何运气。 我目前的方法是:从'myday'获取工作日,然后偏移到星期一。

df['myday'] is column of dates. 
mydays = pd.DatetimeIndex(df['myday']).weekday
df['week_start'] = pd.DatetimeIndex(df['myday']) - pd.DateOffset(days=mydays)

但我明白了 TypeError:timedelta days 组件不支持的类型:numpy.ndarray

如何从 df 列获取周开始日期?

【问题讨论】:

【参考方案1】:
from datetime import datetime, timedelta

# Convert column to pandas datetime equivalent
df['myday'] = pd.to_datetime(df['myday']) 

# Create function to calculate Start Week date
week_start_date = lambda date: date - timedelta(days=date.weekday())

# Apply above function on DataFrame column
df['week_start_date'] = df['myday'].apply(week_start_date)

【讨论】:

【参考方案2】:

虽然@knightofni's 和@Paul's 解决方案都有效,但我倾向于尽量避免在 Pandas 中使用 apply,因为与基于数组的方法相比,它通常非常慢。为了避免这种情况,在转换为日期时间列(通过pd.to_datetime)后,我们可以修改基于工作日的方法,并通过直接转换将星期几转换为numpy timedelta64[D]:

df['week_start'] = df['myday'] - df['myday'].dt.weekday.astype('timedelta64[D]')

或将to_timedelta 用作@ribitskiyb suggested:

df['week_start'] = df['myday'] - pd.to_timedelta(df['myday'].dt.weekday, unit='D'). 

使用包含 60,000 个日期时间的测试数据,我使用新发布的 Pandas 1.0.1 的建议答案得到了以下时间。

%timeit df.apply(lambda x: x['myday'] - datetime.timedelta(days=x['myday'].weekday()), axis=1)
>>> 1.33 s ± 28.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit df['myday'].dt.to_period('W').apply(lambda r: r.start_time)
>>> 5.59 ms ± 138 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df['myday'] - df['myday'].dt.weekday.astype('timedelta64[D]')
>>> 3.44 ms ± 106 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df['myday'] - pd.to_timedelta(df['myday'].dt.weekday, unit='D')
>>> 3.47 ms ± 170 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

这些结果表明,Pandas 1.0.1 显着提高了基于 to_period 应用方法的速度(与 Pandas .astype('timedelta64[D]') 或使用 @ 987654331@ 仍然更胜一筹。基于这些结果,我建议继续使用pd.to_timedelta

【讨论】:

这是如何工作的?我的数据集中的 df['myday'].dt.weekday.astype('timedelta64[D]') 返回一系列全零。为什么或如何从 df['myday'] 中减去 0 起作用?这似乎是最好的解决方案。 澄清上面的帖子,我理解发生了什么的方式基本上是说取日期,然后从中减去星期几。但我不明白为什么 .astype('timedelta64[D]') 会导致全为零。 @DonQuixote 可能您的“我的一天”频率小于“1D”。那时这行不通。您必须修改它以减去小时、分钟等。【参考方案3】:

它失败是因为 pd.DateOffset 需要一个整数作为参数(并且您正在为其提供一个数组)。您只能使用 DateOffset 将日期列更改相同的偏移量。

试试这个:

import datetime as dt
# Change 'myday' to contains dates as datetime objects
df['myday'] = pd.to_datetime(df['myday'])  
# 'daysoffset' will container the weekday, as integers
df['daysoffset'] = df['myday'].apply(lambda x: x.weekday())
# We apply, row by row (axis=1) a timedelta operation
df['week_start'] = df.apply(lambda x: x['myday'] - dt.TimeDelta(days=x['daysoffset']), axis=1)

我还没有实际测试过这段代码(没有示例数据),但这应该适用于您所描述的内容。

但是,您可能想查看 pandas.Resample,它可能会提供更好的解决方案 - 取决于您正在寻找的确切内容。

【讨论】:

谢谢您的解释。这个解决方案正是我想要的! TimeDelta 不应该引用df['daysoffset']吗? 使用 lambda 会导致速度变慢【参考方案4】:

(只是添加到n8yoder的答案)

使用.astype('timedelta64[D]') 对我来说似乎不太可读——找到了只使用 pandas 功能的替代方法:

df['myday'] - pd.to_timedelta(arg=df['myday'].dt.weekday, unit='D')

【讨论】:

我喜欢@Paul 的 lambda 表达式,但这个答案要快得多,而且同样“精练”。感谢您的解决方案。【参考方案5】:

另一种选择:

df['week_start'] = df['myday'].dt.to_period('W').apply(lambda r: r.start_time)

这会将“week_start”设置为“myday”时间之前的第一个星期一。

【讨论】:

谢谢。 df['myday'].dt.to_period('W').dt.start_time 可能比使用 apply 更快(不确定何时引入,可能不适用于旧版 pandas)

以上是关于从 Python(熊猫)中的日期列获取周开始日期(星期一)?的主要内容,如果未能解决你的问题,请参考以下文章

熊猫从python中的日期字符串列获取日期值

熊猫从日期范围列中提取开始和结束日期[重复]

熊猫从日期获取年龄(例如:出生日期)

从SQL Server中的周数获取周开始日期和周结束日期

SQL ORACLE 从多个日期时间行中获取周数

Python2:检索给定日期范围的星期日 - 星期六周开始/结束日期