[英]Pandas: calculate time elapsed between timestamp and current time, but only business hours and with timezone
我正在尝试使用 Pandas 来计算经过的业务秒数。 我在 Pandas dataframe 中有一个列,它在纽约时区有一堆时间戳。 这是我到目前为止的代码:
import pandas as pd
import datetime
times = pd.DataFrame([datetime.datetime.now(timezone('America/New_York')),datetime.datetime.now(timezone('America/New_York'))],columns=['timestamp'])
time.sleep(2)
times['difference'] = (datetime.datetime.now(timezone('America/New_York')) - times)
times['difference'] = times['difference'].dt.seconds
这按预期工作,并在“差异”列中给出了 2 的答案。 但现在我只想包括营业时间(比如上午 9 点到下午 5 点)。 因此,昨天下午 5 点到今天早上 9 点之间的 output 为零。 我已阅读有关时间偏移的 Pandas 文档并寻找类似的问题,但没有找到任何有效的示例。
您可以通过首先使用 Pandas BusinessHour class检查给定的时间戳是否在工作时间内(感谢此线程)然后计算时间差或在时间戳超出工作时间时分配零来实现此目的。
我创建了一个虚拟数据集来测试代码,如下所示:
import pandas as pd
import time
# Sets the timezone
timezone = "America/New_York"
# Gets business hours from native Pandas class
biz_hours = pd.offsets.BusinessHour()
# Creates array with timestamps to test code
times_array = pd.date_range(start='2021-05-18 16:59:00', end='2021-05-18 17:01:00',
tz=timezone, freq='S')
# Creates DataFrame with timestamps
times = pd.DataFrame(times_array,columns=['timestamp'])
# Checks if a timestamp falls within business hours
times['is_biz_hour'] = times['timestamp'].apply(pd.Timestamp).apply(biz_hours.onOffset)
time.sleep(2)
# Calculates the time delta or assign zero, as per business hour condition
times['difference'] = (times.apply(lambda x: (pd.Timestamp.now(tz=timezone) - x['timestamp']).seconds
if x['is_biz_hour'] else 0,
axis=1))
output目前并不完美,因为它从现在的时间中减去了时间戳,因此相差很大:
timestamp is_biz_hour difference
57 2021-05-18 16:59:57-04:00 True 71238
58 2021-05-18 16:59:58-04:00 True 71237
59 2021-05-18 16:59:59-04:00 True 71236
60 2021-05-18 17:00:00-04:00 True 71235
61 2021-05-18 17:00:01-04:00 False 0
62 2021-05-18 17:00:02-04:00 False 0
63 2021-05-18 17:00:03-04:00 False 0
64 2021-05-18 17:00:04-04:00 False 0
但是,您可以看到下午 5 点之后的时间戳有 0 的差异,而其他时间戳有一个有效的差异。
这是一种数学方法。
让我们研究一下 TimeStamp some_time
的工作秒数。 我们将在午夜需要some_time
的时间戳。 我们称之为d_day
:
d_day = some_time.replace(hour=0, minute=0, second=0, microsecond=0)
首先,让我们定义我们的工作日。 它从上午9 AM
开始,一直持续到5 PM
。 在几秒钟内,它给出:
start_time = 9*3600
max_work_time = (17-9) * 3600
现在,让我们获取现在的时间戳和今天午夜的时间戳,以秒为单位。
now = datetime.now()
today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0)
seconds_today = (now - today).seconds
为了获得今天的工作秒数,我们必须减去start_time
然后保持在max_work_time
:
worked_seconds_today = min(seconds_today - start_time, max_work_time)
但是当然,我们只想在today
和d-day
是不同的日子时才保留它,否则我们想计算自some_time
以来的工作秒数:
secs_today = min(seconds_today - start_time, max_work_time) \
if today > d_day \
else min(seconds_today - start_time, max_work_time) - min((some_time - today).seconds - start_time, max_work_time)
我们只想在这里工作一整天。 所以让我们简单地做:
inbetween_days = max((datetime.today() - d_day).days - 1, 0)
现在我们可以通过这样做简单地计算全天的工作秒数:
secs_inbetween_days = inbetween_days * max_work_time
最后,我们还想要 d_day 自some_time
以来的工作秒数。 我们可以应用与今天相同的逻辑。 如果今天和some_time
是同一天,我们只需输入零,正如我们今天已经计算的那样。
def worked_secs(x, since):
return min((x - since).seconds - start_time, max_work_time)
secs_day_d = max_work_time - worked_secs(some_time, d_day) if today != d_day else 0
总数是前面三个部分的总和:
total = secs_day_d + secs_inbetween_days + secs_today
def busy_seconds(some_time):
# Outside the function is OK also
start_time = 9*3600
max_work_time = (17-9)*3600
# We must calculate all times with the same timezone
tz = some_time.tz
now = datetime.now(tz=tz) # now
today = now.replace(hour=0, minute=0, second=0, microsecond=0) # today at midnight
d_day = some_time.replace(hour=0, minute=0, second=0, microsecond=0) # d-day at midnight
def worked_secs(x, since): # a function is more convenient
return min((x - since).seconds - start_time, max_work_time)
n_complete_days = max((today - d_day).days - 1, 0)
secs_day_d = max_work_time - worked_secs(some_time, d_day) if today != d_day else 0
secs_inbetween_days = max_work_time * n_complete_days
secs_today = worked_secs(now, today) \
if d_day < today \
else worked_secs(now, today) - worked_secs(some_time, today)
return secs_day_d + secs_inbetween_days + secs_today
我们可以将此 function 应用于列:
times.timestamp.apply(busy_seconds)
# > (Example)
# 0 67420800
# 1 57340800
# 2 28800
# Name: timestamp, dtype: int64
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.