= LightSchedule(100.0) constant_schedule
Lights
LightSchedule.__add__
LightSchedule.__add__ (schedule:__main__.LightSchedule)
Calculate the sum of the two LightSchedules
Type | Details | |
---|---|---|
schedule | LightSchedule | another LightSchedule object |
Returns | LightSchedule |
LightSchedule.__sub__
LightSchedule.__sub__ (schedule:__main__.LightSchedule)
Calculate the difference between two LightSchedules
Type | Details | |
---|---|---|
schedule | LightSchedule | another LightSchedule object |
Returns | LightSchedule |
Overview
The Lights module streamlines the process of creating light schedules for circadian models. Its main class is LightSchedule
which facilitates creating light functions of time. LightSchedule
objects can be added, subtracted, and concatenated between each other to create custom light schedules.
Creation and visualization
Light schedules can be created by passing either a float
value or a function to the LightSchedule
constructor. For example, we can create a schedule of constant light by:
and plot it between 0 and 72 hours using the plot
method:
= constant_schedule.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)');
ax.set_ylabel(0.0, 120.0); ax.set_ylim(
If we want to create a schedule that varies over time, we can pass a function to the constructor:
def smooth_pulse_function(time):
= np.tanh((time - 7.0))
rise = np.tanh((time - 20.0))
fall = 100 * (rise - fall) / 2.0
y return y
= LightSchedule(smooth_pulse_function)
smooth_pulse = smooth_pulse.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
Additionally, the LightSchedule
constructor accepts a period
argument which allows us to specify the periodicity of the schedule. For example, we can make the smooth pulse repeat every 24 hours:
= LightSchedule(smooth_pulse_function, period=24.0)
smooth_pulse = smooth_pulse.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
Typically, we would like to create schedules that are composed of on-and-off pulses of light. We can do this via the LightSchedule.from_pulse()
function. For example, we can create a periodic schedule with 16 hours of light and 8 hours of darkness by:
= LightSchedule.from_pulse(100.0, start=8, duration=16.0, period=24.0)
on_off_schedule = on_off_schedule.plot(0.0, 72.0)
ax -5, 110);
ax.set_ylim('Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
see the API documentation for a description of the parameters that can be passed to LightSchedule.from_pulse()
, including adding a baseline to the pulse via baseline
.
The resulting LightSchedule
objects are callable and return the value of the schedule at the specified times
= np.linspace(0.0, 72.0, 10)
times print(on_off_schedule(times))
[ 0. 100. 100. 0. 100. 100. 0. 100. 100. 0.]
Addition and subtraction
LightSchedule
objects can be combined using the +
and -
operators. For example, we can add a pulse of light at 40 hours to our on_off_schedule
by:
= LightSchedule.from_pulse(20.0, start=40, duration=2.0)
single_pulse = on_off_schedule + single_pulse
combined_schedule = combined_schedule.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
or subtract it:
= on_off_schedule - single_pulse
combined_schedule = combined_schedule.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
Concatenation
LightSchedule
objects can be concatenated in time using the concatenate_at()
method. For example, we can create a schedule where the first 48 hours consist of 8 hours of darkness and 16 hours of light, but after that the schedule is constant at 50 lux:
= LightSchedule.from_pulse(100.0, start=8, duration=16.0, period=24.0)
regular_schedule = LightSchedule(50.0)
constant_light = regular_schedule.concatenate_at(constant_light, 48.0)
concatenated_schedule = concatenated_schedule.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
The approach works for all types of schedules. For example, we can concatenate smooth_pulse
to on_off_schedule
at 28 hours by:
= on_off_schedule.concatenate_at(smooth_pulse, 28.0)
on_off_to_smooth = on_off_to_smooth.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
It is important to note that the default behavior of schedule_1.concatenate_at(schedule_2, timepoint)
is to shift the time=0
of schedule_2
to timepoint
. This can be appreciated if we concatenate the same schedules as above, but with timepoint=40
:
= on_off_schedule.concatenate_at(smooth_pulse, 40.0)
on_off_to_smooth = on_off_to_smooth.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
we see that the second on-and-off pulse is suddenly interrupted by the beginning of the smoot pulse. This is because the time=0
of smooth_pulse
is shifted to timepoint=40
. If we want to concatenate the schedules without shifting the time=0
of schedule_2
(keep the time=0
of schedule_2
aligned with that of schedule_1
), we can pass shift=False
to the concatenate_at()
method:
= on_off_schedule.concatenate_at(smooth_pulse, 40.0, shift_schedule=False)
on_off_to_smooth = on_off_to_smooth.plot(0.0, 72.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)'); ax.set_ylabel(
Typical light schedules
The LightSchedule
class implements several helper functions to obtain common light schedules:
LightSchedule.Regular
: light of a typical dayLightSchedule.ShiftWork
: approximation of the usual light for shift workersLightSchedule.SlamShift
: light pattern that slam shift workers experience when changing shiftsLightSchedule.SocialJetlag
: light pattern experienced by staying up late on weekends
Check out the API documentation for a description of the parameters that can be passed to each function.
Regular light
LightSchedule.Regular
is a typical light schedule that repeats every 24 hours. The default schedule is 16 hours of light and 8 hours of darkness, but this can be changed by passing lights_on
and lights_off
to the constructor.
= LightSchedule.Regular()
regular_light = regular_light.plot(0.0, 24*7.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)');
ax.set_ylabel(16, 4);
ax.figure.set_size_inches(# add a vertical line to the start of each day in dashed gray
for day in range(8):
*24.0, color='gray', linestyle='--');
ax.axvline(day0, 24.0*8.0, 12));
ax.set_xticks(np.arange(0.0, 24.0*7.0); ax.set_xlim(
Shift worker
LightSchedule.ShiftWork
approximates what a typical light schedule looks for some shift workers. The schedule is periodic over a whole work week (determined by the sum of days_on
and days_off
) and implements transitions between workdays and days off. The default schedule is:
= LightSchedule.ShiftWork()
shift_schedule = shift_schedule.plot(0.0, 24.0*8.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)');
ax.set_ylabel(16, 4);
ax.figure.set_size_inches(# add a vertical line to the start of each day in dashed gray
for day in range(8):
*24.0, color='gray', linestyle='--');
ax.axvline(day0, 24.0*8.0, 12));
ax.set_xticks(np.arange(= 5
days_on = 2
days_off = 9.0
lights_off_workday = 17.0
lights_on_workday = lights_off_workday + 24*(days_on-1)
time_last_workday = lights_on_workday + 24.0*(days_on + days_off - 1)
time_last_day_off = time_last_day_off + lights_on_workday
time_first_workday # set background color to light red for the workdays
0.0, time_last_workday, facecolor='r', alpha=0.1, label='Workdays');
ax.axvspan(='r', alpha=0.1);
ax.axvspan(time_last_day_off, time_first_workday, facecolor# set background color to light green for days off
='g', alpha=0.1, label='Days off');
ax.axvspan(time_last_workday, time_last_day_off, facecolor# set limits to be a week
0.0, 24.0*7.0);
ax.set_xlim(# place legend at the top of the plot
='upper center', bbox_to_anchor=(0.5, 1.15), ncol=2); ax.legend(loc
Slam shift
LightSchedule.SlamShift
approximates the light schedule that slam shift workers experience when changing shifts. The schedule starts with before_days
where the worker is on a regular schedule and then shifts to a new schedule by shift
hours. Before the shift happens, there’s a transition via sleep banking. The default schedule is:
= LightSchedule.SlamShift()
slam_shift = slam_shift.plot(0.0, 24.0*12.0)
ax 'Time (hours)');
ax.set_xlabel('Light (lux)');
ax.set_ylabel(16, 4);
ax.figure.set_size_inches(# add a vertical line to the start of each day in dashed gray
for day in range(13):
*24.0, color='gray', linestyle='--');
ax.axvline(day0, 24.0*12.0, 12));
ax.set_xticks(np.arange(# set background color to light red for the days before the shift
0.0, 24.0*5, facecolor='r', alpha=0.1, label='Before shift');
ax.axvspan(# set background color to light green for days after the shift
24.0*5, 24.0*12.0, facecolor='g', alpha=0.1, label='After shift');
ax.axvspan(# place legend at the top of the plot
='upper center', bbox_to_anchor=(0.5, 1.15), ncol=2);
ax.legend(loc0.0, 24.0*12.0); ax.set_xlim(
Light schedules used in circadian experiments
The LightSchedule
class also implements light schedules that are used in circadian experiments: - LightSchedule.Hilaire12
: Implements the schedule from Hilaire et al. 2012 to study the effect of bright light pulses on circadian rhythms
Check out the API documentation for more details.
Light protocol from St. Hilaire et al. 2012
LightSchedule.Hilaire12
implements the light protocol used in Hilaire et al. 2012. The schedule consists of: - A first baseline day with 24 hours of regular_lux=90
lux - A second baseline day with 16 hours of regular_lux=90
lux and 8 hours of darkness - A third day with 16 hours of light and 8 hours of darkness. The 16 hours of light are divided into 8 hours of regular_lux=90
lux and 8 hours of constant_routine_lux=3
lux. - 8 hours of darkness - A constant routine region of first_constant_routine_duration
hours with constant_routine_lux=3
lux - 8 hours of darkness - 16 hours of constant_routine_lux=3
lux with a pulse of pulse_lux=8000
lux and pulse_duration
hours at the middle of the light period (the pulse’s center is aligned with the center of the light period) - 8 hours of darkness - second_constant_routine_duration
hours of constant_routine_lux=3
lux
The only required parameters are first_constant_routine_duration
and second_constant_routine_duration
which help time the pulse of light relative to the circadian phase. The other parameters have default values following the original paper.
= LightSchedule.Hilaire12(30, 48)
hilaire_schedule = np.linspace(0, 200, 2000)
time = hilaire_schedule(time)
light_values
plt.plot(time, light_values)'Light (lux)')
plt.ylabel('Time (hours)')
plt.xlabel('log')
plt.yscale(
= plt.gca()
ax 12, 4);
ax.figure.set_size_inches(0.0, 24*3, facecolor='r', alpha=0.3, label='Baseline days');
ax.axvspan(24.0, color='gray', linestyle='--');
ax.axvline(48.0, color='gray', linestyle='--');
ax.axvline(72.0, color='gray', linestyle='--');
ax.axvline(24*3 + 8, 24*3 + 8 + 30, facecolor='g', alpha=0.3, label='First constant routine (CR1)');
ax.axvspan(24*3 + 8 + 30 + 8 + 7.5,
ax.axvspan(24*3 + 8 + 30 + 8 + 8.5, facecolor='y', alpha=0.3, label='Pulse');
24*3 + 8 + 30 + 8 + 16 + 8,
ax.axvspan(24*3 + 8 + 30 + 8 + 16 + 8 + 48, facecolor='tab:purple', alpha=0.3, label='Second constant routine (CR2)');
='upper center', bbox_to_anchor=(0.5, 1.15), ncol=4);
ax.legend(loc0.0, 24*3 + 8 + 30 + 8 + 16 + 8 + 48);
ax.set_xlim( plt.show()
Light protocol from Chang et al. 2014
LightSchedule.Chang14
implements the light protocol used in Chang et al. 2014. In this study, authors explore the effect of light-emitting eBooks on sleep. The schedule spans a total of 14 days. Each week has the same schedule except for the type of light received on reading sessions: - A first day with no reading and dim light between noon and 10pm - A second day with dim light for 6 hours after wake up time (6am), and a reading session on dim light 4 hours before bed (10pm bedtime). The light received from the reading devices is controlled by ereader_lux
and book_lux
. Which condition happens on the first week is set by first_reading_condition
. - Four days of reading consisting of typical_indoor_lux
between 6am and 6pm followed by a reading session under dim_lux
plus the light received from reading devices
= LightSchedule.Chang14()
chang_schedule = np.linspace(0, 24 * 14, 100 * 14)
time = chang_schedule(time)
light_values
plt.plot(time, light_values)'Light (lux)')
plt.ylabel('Time (hours)')
plt.xlabel(
= plt.gca()
ax 12, 4)
ax.figure.set_size_inches(
# axvline every 24 hours
for i in range(15):
* 24, color='gray', linestyle='--')
ax.axvline(i # ebook
for i in range(5):
24 * (i + 1) + 18, 24 * (i + 1) + 18 + 4, facecolor='r', alpha=0.3)
ax.axvspan(# book
for i in range(5):
24 * (i + 7) + 18, 24 * (i + 7) + 18 + 4, facecolor='g', alpha=0.3)
ax.axvspan(# Constant procedure
# Create legend for book and ebook
0, 0, facecolor='r', alpha=0.3, label='eReader')
ax.axvspan(0, 0, facecolor='g', alpha=0.3, label='Book')
ax.axvspan(='upper center', bbox_to_anchor=(0.5, 1.15), ncol=2)
ax.legend(loc
plt.show()
API documentation
LightSchedule
LightSchedule (light:Callable[[float],float], period:float=None)
Helper class for creating light schedules
Type | Default | Details | |
---|---|---|---|
light | Callable | function that takes in a time value and returns a float, if a float is passed, then the light function is a constant set to that lux value | |
period | float | None | period in hours, if None, then the light pulse is not repeated. Must be positive |
Returns | None |
LightSchedule.from_pulse
LightSchedule.from_pulse (lux:float, start:float, duration:float, period:float=None, baseline:float=0.0)
Define a light schedule with a single (or a repetitive) light pulse
Type | Default | Details | |
---|---|---|---|
lux | float | light intensity of the pulse in lux. Must be nonnegative | |
start | float | start time in hours | |
duration | float | duration in hours. Must be positive | |
period | float | None | period in hours, if None, then the light pulse is not repeated. Must be positive |
baseline | float | 0.0 | baseline intensity outside of the light pulse in lux. Must be nonnegative |
Returns | LightSchedule |
LightSchedule.concatenate_at
LightSchedule.concatenate_at (schedule:__main__.LightSchedule, timepoint:float, shift_schedule:bool=True)
Concatenate two LightSchedules at the provided timepoint. When shift_schedule=True
, schedule
is shifted in time by timepoint
. Not shifted otherwise
Type | Default | Details | |
---|---|---|---|
schedule | LightSchedule | another LightSchedule object | |
timepoint | float | timepoint (in hours) at which schedules are concatenated | |
shift_schedule | bool | True | if True, then the schedule is shifted by the timepoint value |
Returns | LightSchedule |
LightSchedule.plot
LightSchedule.plot (plot_start_time:float, plot_end_time:float, num_samples:int=10000, ax=None, *args, **kwargs)
Plot the light function between start_time
and end_time
with num_samples
samples. Accepts matplotlib
*args
and **kwargs
Type | Default | Details | |
---|---|---|---|
plot_start_time | float | start time of the plot in hours | |
plot_end_time | float | end time of the plot in hours | |
num_samples | int | 10000 | number of samples to plot |
ax | NoneType | None | matplotlib axis to plot on |
args | |||
kwargs | |||
Returns | Axes |
LightSchedule.Regular
LightSchedule.Regular (lux:float=150.0, lights_on:float=7.0, lights_off:float=23.0)
Create a regular light and darkness 24 hour schedule
Type | Default | Details | |
---|---|---|---|
lux | float | 150.0 | intensity of the light in lux |
lights_on | float | 7.0 | time of the day for lights to come on in hours |
lights_off | float | 23.0 | time of the day for lights to go off in hours |
Returns | LightSchedule |
LightSchedule.ShiftWork
LightSchedule.ShiftWork (lux:float=150.0, days_on:int=5, days_off:int=2, lights_on_workday:float=17.0, lights_off_workday:float=9.0, lights_on_day_off:float=9.0, lights_off_day_off:float=24.0)
Create a light schedule for a shift worker
Type | Default | Details | |
---|---|---|---|
lux | float | 150.0 | lux intensity of the light. Must be a nonnegative float or int |
days_on | int | 5 | number of days on the night shift. Must be a positive int |
days_off | int | 2 | number of days off shift. Must be a positive int |
lights_on_workday | float | 17.0 | hour of the day for lights to come on on a workday. Must be between 0.0 and 24.0 |
lights_off_workday | float | 9.0 | hour of the day for lights to go off on a workday. Must be between 0.0 and 24.0 |
lights_on_day_off | float | 9.0 | hour of the day for lights to come on on a day off. Must be between 0.0 and 24.0 |
lights_off_day_off | float | 24.0 | hour of the day for lights to go off on a day off. Must be between 0.0 and 24.0 |
Returns | LightSchedule |
LightSchedule.SlamShift
LightSchedule.SlamShift (lux:float=150.0, shift:float=8.0, before_days:int=5, starting_lights_on:float=7.0, starting_lights_off:float=23.0)
Create a light schedule for a shift worker under a slam shift
Type | Default | Details | |
---|---|---|---|
lux | float | 150.0 | intensity of the light in lux |
shift | float | 8.0 | shift in the light schedule in hours |
before_days | int | 5 | days before the shift occurs |
starting_lights_on | float | 7.0 | time of the day for lights to come on |
starting_lights_off | float | 23.0 | time of the day for lights to go off |
Returns | LightSchedule |
LightSchedule.Hilaire12
LightSchedule.Hilaire12 (first_constant_routine_duration:float, second_constant_routine_duration:float, regular_lux:float=90, constant_routine_lux:float=3, pulse_duration:float=1, pulse_lux:float=8000)
Create a light schedule matching the Hilaire et al. 2012 experimental protocol. Does not include baseline days.
Type | Default | Details | |
---|---|---|---|
first_constant_routine_duration | float | duration of the constant routine in hours | |
second_constant_routine_duration | float | duration of the second constant routine in hours | |
regular_lux | float | 90 | intensity of the light in lux before the constant routine |
constant_routine_lux | float | 3 | intensity of the light in lux during the constant routine |
pulse_duration | float | 1 | duration of the light pulse in hours |
pulse_lux | float | 8000 | intensity of the light pulse in lux |
Returns | LightSchedule |
LightSchedule.Chang14
LightSchedule.Chang14 (dim_lux:float=3.0, typical_indoor_lux:float=90.0, ereader_lux:float=31.73, book_lux:float=0.91, first_reading_condition:str='eReader', reading_start_time:float=18.0, reading_duration:float=4.0)
Create a light schedule matching the Chang et al. 2014 experimental protocol for studying the effect of eReaders on sleep.
Type | Default | Details | |
---|---|---|---|
dim_lux | float | 3.0 | intensity of the light in reading sessions and constant protocols |
typical_indoor_lux | float | 90.0 | intensity of the light during wakefulness |
ereader_lux | float | 31.73 | intensity of the light during the eReader session. Photopic lux value taken from article |
book_lux | float | 0.91 | intensity of the light during the book reading session. Photopic lux value taken from article |
first_reading_condition | str | eReader | Reading condition for the first set of days. Second reading condition is the opposite |
reading_start_time | float | 18.0 | time of the day when the reading sessions start |
reading_duration | float | 4.0 | duration of the reading sessions in hours |
Returns | LightSchedule |
Social jet lag
LightSchedule.SocialJetlag
implements a light schedule where the person stays up late on weekends while maintaining a regular schedule during the week. The schedule is periodic over the sum ofnum_regular_days
andnum_jetlag_days
.