这是我最近在我的程序中遇到的一个计算:
给定2个日期和一个明暗周期,计算在这些日期之间出现的明暗周期的(分数)数。
def foo(start_date, end_date, lights_on, lights_off):
#stuff...
return (daytimes, nighttimes)
start_date
和end_date
是具有(至少)分钟精度的日期时间对象lights_on
和lights_off
是int
,分别代表每天打开和关闭灯光的时间。这些是定义“点亮”和“熄灭”时间段的持续时间。lights_on
和lights_off
可以不均等地划分日期(即不必是12H光照:12H黑暗),而光照周期不能不必在白天和晚上都匹配“真正的” (例如,可以在早上7点关闭灯光,在晚上7点打开灯光)。但是,假定照明周期将是恒定的(即,想象房间或建筑物中的照明,而不是日出/日落),并且照明始终在打开时打开或关闭。小时(例如,晚上8:30灯光不能改变)。float
表示两个日期之间已完成的点亮和熄灭期间的数量。示例:
import pandas as pd
date1 = pd.Timestamp(year=2020, month=1, day=1, hour=9, minute=0)
date2 = pd.Timestamp(year=2020, month=1, day=3, hour=13, minute=0)
date3 = pd.Timestamp(year=2020, month=1, day=5, hour=20, minute=47)
foo(date1, date2, 9, 17)
#returns (2.5, 2)
foo(date1, date3, 9, 17)
#returns (5, 4.236...)
foo(date1, date2, 19, 7)
#returns (2, 2.333...)
说明:
date1
和date2
。date1
和date3
之间一个暗周期长度的0.236 ...倍。date1
和{之间,从1月2日的7:00到17:00的周期+ 1月3日的7:00至13:00的黑暗周期的6/12 = 28/12 = 2.333 ....) {1}}。对我来说,这种计算很容易概念化但很难编码。我有一个有效的答案(在下面发布),但我为此苦苦挣扎了很长时间,希望看到其他更好的方法。如果有可用的现有库很好,并且我不介意只要输出正确,数据的表示方式是否不同。
答案 0 :(得分:1)
这是另一种解决方案,它尝试通过将开始时间和结束时间偏移lights_on
和lights_off
的最小值来简化数学,以便每天的周期实际上由{{ 1}}。我已经使用abs(lights_on - lights_off)
对象完成了所有数学运算。
datetime.timedelta
输出:
from datetime import datetime, time, timedelta
def cycle_times(start_date, end_date, lights_on):
start_time = timedelta(hours=start_date.hour, minutes=start_date.minute)
lights_on_time = timedelta(hours=lights_on)
# generate the cycles for the first day
off_time = max(lights_on_time - start_time, timedelta(seconds = 0))
on_time = timedelta(hours=24) - max(lights_on_time, start_time)
cycle_start = start_date.replace(hour=0, minute=0) + timedelta(days=1)
# add a complete cycle for each day before end_date
while cycle_start.date() < end_date.date():
cycle_start += timedelta(days=1)
off_time += lights_on_time
on_time += timedelta(hours=24-lights_on)
# generate the partial cycles for the last day
end_time = timedelta(hours=end_date.hour, minutes=end_date.minute)
off_time += min(end_time, lights_on_time)
on_time += max(end_time - lights_on_time, timedelta(seconds=0))
return (off_time.total_seconds() / 3600, on_time.total_seconds() / 3600)
def get_daynight_count(start_time, end_time, lights_on, lights_off):
if lights_on > lights_off:
off_period = lights_on - lights_off
on_period = 24 - off_period
offset = timedelta(hours=-lights_off)
off_time, on_time = cycle_times(start_time + offset, end_time + offset, lights_on - lights_off)
else:
on_period = lights_off - lights_on
off_period = 24 - on_period
offset=timedelta(hours=-lights_on)
on_time, off_time = cycle_times(start_time + offset, end_time + offset, lights_off - lights_on)
return (on_time / on_period, off_time / off_period)
date1 = datetime(year=2020, month=1, day=1, hour=9, minute=0)
date2 = datetime(year=2020, month=1, day=3, hour=13, minute=0)
date3 = datetime(year=2020, month=1, day=5, hour=20, minute=47)
print(get_daynight_count(date1, date2, 9, 17))
print(get_daynight_count(date1, date3, 9, 17))
print(get_daynight_count(date1, date2, 19, 7))
更新
只需使用小时/分钟数学((2.5, 2.0)
(5.0, 4.236458333333333)
(2.0, 2.3333333333333335)
)即可简化cycle_times
代码:
1 hour = 60 minutes
答案 1 :(得分:0)
以下是以下代码中使用的步骤:
date2
,该数组以cuts
开头,以start_time
结尾,并且在每次灯光变化的时候都位于两者之间end_time
中的每个时间(cuts
除外),检查它是在“白天”还是“晚上”,并计算到下一次的时间差。end_date
对于以上示例:
import pandas as pd
import datetime as dt
#step 1
def is_day_or_night(time, period, lights_on=7, lights_off=19):
lights_on = dt.time(hour=lights_on)
lights_off = dt.time(hour=lights_off)
val = False
#defaults to checking if at night
if lights_off > lights_on:
val = time.time() >= lights_off or time.time() < lights_on
elif lights_off < lights_on:
val = time.time() >= lights_off and time.time() < lights_on
#reverses if period='day'
return val if period=='night' else not val
def get_daynight_count(start_time, end_time, lights_on, lights_off):
#step 2
cuts = []
cuts.append(start_time)
loop_time = start_time.replace(minute=0,second=0)
while loop_time < end_time:
loop_time += pd.Timedelta(hours=1)
if loop_time.hour == lights_on:
cuts.append(loop_time)
elif loop_time.hour == lights_off:
cuts.append(loop_time)
cuts.append(end_time)
days = []
nights = []
#step 3
if lights_off > lights_on:
day_hours = lights_off - lights_on
night_hours = 24 - day_hours
else:
night_hours = lights_on - lights_off
day_hours = 24 - night_hours
day_hours = pd.Timedelta(hours = day_hours)
night_hours = pd.Timedelta(hours = night_hours)
#step 4 and 5
for i, t in enumerate(cuts[:-1]):
if is_day_or_night(t, 'day', lights_on, lights_off):
days.append((cuts[i+1] - t)/day_hours)
else:
nights.append((cuts[i+1] - t)/night_hours)
#step 6
return (sum(days),sum(nights))