Usage of sleep diary with pyActigraphy

Sleep diary is usually given with an actimeter to allow participants to report the subjective evaluation of their sleep episodes. A sleep diary is the easiest and least invasive way to get information about sleep episodes from participants. It allows comparison between data recorded by an actigraph and the real-life.

In medical fields, sleep diary is commonly recommended in order to help doctors in the diagnosis and treatment of sleep-wake cycle disorders.

[1]:
import pyActigraphy
[2]:
import os
[3]:
import plotly.graph_objs as go
[4]:
# Retrieve path to example files
fpath = os.path.join(os.path.dirname(pyActigraphy.__file__),'tests/data/')
[5]:
# Read test file
raw = pyActigraphy.io.read_raw_awd(fpath+'example_01.AWD')
[6]:
# Check the start time of the actigraphy recording
raw.start_time
[6]:
Timestamp('1918-01-23 13:58:00')
[7]:
# Check the duration of the recording
raw.duration()
[7]:
Timedelta('12 days 18:41:00')

Read a sleep diary

Sleep diary class has been created to read a sleep diary file. Sleep diary file must be formatted as the following:

SubjectID

your_subject_id

Type

Start

End

Night/Nap/NoWear

YYYY-MM-DD HH:MM

YYYY-MM-DD HH:MM

As an illustration, let us first read the sleep diary of example 01:

[8]:
sleep_diary = raw.read_sleep_diary(fpath + 'example_01_sleepdiary.ods')

Subject’s name in the sleep diary is easily verified:

[9]:
raw.sleep_diary.name
[9]:
'EXAMPLE_01'

To access to the data contained in the sleep diary:

[10]:
raw.sleep_diary.diary
[10]:
TYPE START END
0 NAP 1918-01-24 13:00:00 1918-01-24 13:45:00
1 NIGHT 1918-01-24 23:00:00 1918-01-25 07:00:00
2 NAP 1918-01-25 13:00:00 1918-01-25 13:35:00
3 NIGHT 1918-01-25 22:00:00 1918-01-26 07:30:00
4 NAP 1918-01-26 13:10:00 1918-01-26 14:00:00
5 NIGHT 1918-01-27 00:00:00 1918-01-27 07:30:00
6 NAP 1918-01-27 13:00:00 1918-01-27 13:45:00
7 NIGHT 1918-01-27 23:20:00 1918-01-28 05:00:00
8 NOWEAR 1918-01-28 12:00:00 1918-01-28 12:30:00
9 NAP 1918-01-28 16:00:00 1918-01-28 17:30:00
10 NIGHT 1918-01-28 22:30:00 1918-01-29 06:15:00
11 NAP 1918-01-29 13:15:00 1918-01-29 14:00:00
12 NIGHT 1918-01-29 23:20:00 1918-01-30 07:00:00
13 NAP 1918-01-30 13:00:00 1918-01-30 13:45:00
14 NIGHT 1918-01-30 23:15:00 1918-01-31 06:45:00
15 NAP 1918-01-31 13:15:00 1918-01-31 14:00:00
16 NIGHT 1918-01-31 23:15:00 1918-02-01 07:00:00
17 NAP 1918-02-01 13:00:00 1918-02-01 13:45:00
18 NOWEAR 1918-02-01 20:40:00 1918-02-01 21:33:00
19 NIGHT 1918-02-01 23:20:00 1918-02-02 08:00:00
20 NAP 1918-02-02 13:15:00 1918-02-02 14:15:00
21 NIGHT 1918-02-02 23:20:00 1918-02-03 07:45:00

A summary function is available. It returns the count/mean/std/min/25%/50%/75%/max of the durations found for each state in the sleep diary:

[11]:
raw.sleep_diary.summary()
[11]:
count mean std min 25% 50% 75% max
TYPE
NAP 10 0 days 00:50:30 0 days 00:15:10.494371207 0 days 00:35:00 0 days 00:45:00 0 days 00:45:00 0 days 00:48:45 0 days 01:30:00
NIGHT 10 0 days 07:50:30 0 days 00:59:19.353873949 0 days 05:40:00 0 days 07:32:30 0 days 07:45:00 0 days 08:18:45 0 days 09:30:00
NOWEAR 2 0 days 00:41:30 0 days 00:16:15.807358037 0 days 00:30:00 0 days 00:35:45 0 days 00:41:30 0 days 00:47:15 0 days 00:53:00

The mean and standard deviation for one state is accessible by state_infos function:

[12]:
raw.sleep_diary.state_infos('NIGHT')
[12]:
(Timedelta('0 days 07:50:30'), Timedelta('0 days 00:59:19.353873949'))

Specific functions are implemented to directly estimate the mean and standard deviation for theses states: * bed time * nap time * no wear

[13]:
raw.sleep_diary.total_bed_time()
[13]:
(Timedelta('0 days 07:50:30'), Timedelta('0 days 00:59:19.353873949'))
[14]:
raw.sleep_diary.total_nap_time()
[14]:
(Timedelta('0 days 00:50:30'), Timedelta('0 days 00:15:10.494371207'))
[15]:
raw.sleep_diary.total_nowear_time()
[15]:
(Timedelta('0 days 00:41:30'), Timedelta('0 days 00:16:15.807358037'))

Plot the data with sleep diary

First, create a layout for the plot and then plot data.

Shapes are created for each sleep episodes and are meant to be overlaid with the actimetry data. Different parameters (e.g. colours) enable to distinguish sleep during night-time and nap episode.

All shapes are created from a template:

[16]:
raw.sleep_diary.shaded_area
[16]:
{'type': 'rect',
 'xref': 'x',
 'yref': 'paper',
 'x0': 0,
 'y0': 0,
 'x1': 1,
 'y1': 1,
 'fillcolor': '',
 'opacity': 0.5,
 'layer': 'below',
 'line': {'width': 0}}

Then plot actimetry data with the corresponding sleep diary, by adapting the layout:

[17]:
layout = go.Layout(
    title="Actigraphy data",
    xaxis=dict(title="Date time"),
    yaxis=dict(title="Counts/period"),
    shapes=raw.sleep_diary.shapes(),
    showlegend=False
)
[18]:
go.Figure(data=go.Scatter(x=raw.data.index, y=raw.data), layout=layout)

Features of shaded areas can easily be accessed and modified. Three types of modifications are feasible:

  • Global modifications : change parameters for all shaded areas (by changing the shape template)

  • One state modifications : change parameters specific for one state

  • Local modifications : change parameters for the selected shaded area

Global modifications

[19]:
raw.sleep_diary.shaded_area['opacity'] = 1
[20]:
layout.update(shapes=raw.sleep_diary.shapes());
[21]:
go.Figure(data=go.Scatter(x=raw.data.index, y=raw.data), layout=layout)
[22]:
# Access to colours for each state
raw.sleep_diary.state_colour
[22]:
{'NAP': '#7bc043', 'NIGHT': '#d3d3d3', 'NOWEAR': '#ee4035'}

One state modification

[23]:
raw.sleep_diary.state_colour['NIGHT'] = 'rgb(140,95,148)'
[24]:
# Check colour modification
raw.sleep_diary.state_colour
[24]:
{'NAP': '#7bc043', 'NIGHT': 'rgb(140,95,148)', 'NOWEAR': '#ee4035'}
[25]:
layout.update(shapes=raw.sleep_diary.shapes());
[26]:
go.Figure(data=go.Scatter(x=raw.data.index, y=raw.data), layout=layout)

Local modifications of the first shape

[27]:
shapes = raw.sleep_diary.shapes()
shapes[0]['fillcolor'] = 'rgb(0,255,200)'
[28]:
layout.update(shapes=shapes);
[29]:
go.Figure(data=go.Scatter(x=raw.data.index, y=raw.data), layout=layout)

Add custom states

For conveniency, four states (active, nap, night, nowear) are automatically implemented when a sleep diary is read. However, the pyActigraphy package allows users to customise colours and to add states.

[30]:
# Default implemented states
raw.sleep_diary.state_index
[30]:
{'ACTIVE': 2, 'NAP': 1, 'NIGHT': 0, 'NOWEAR': -1}

To add a custom state, all indices and their colours have to be specified during the reading of the sleep diary:

[31]:
raw.read_sleep_diary(
    fpath + 'example_01_sleepdiary_extra_states.ods',
    state_index=dict(ACTIVE=2, NAP=1, NIGHT=0, NOWEAR=-1, AWAKE_IN_BED=3),
    state_colour=dict(
        NAP='#7bc043',
        NIGHT='#d3d3d3',
        NOWEAR='#ee4035',
        AWAKE_IN_BED='rgb(143, 19, 131)'
    )
)
[32]:
layout = go.Layout(
    title="Actigraphy data",
    xaxis=dict(title="Date time"),
    yaxis=dict(title="Counts/period"),
    shapes=raw.sleep_diary.shapes(),
    showlegend=False)
[33]:
go.Figure(data=go.Scatter(x=raw.data.index, y=raw.data), layout=layout)

Mean and standard deviation for the added state are now also accessible:

[34]:
raw.sleep_diary.state_infos('AWAKE_IN_BED')
[34]:
(Timedelta('0 days 01:30:00'), NaT)

Et voilà! Easy, isn’t it?