Introduction to data masking with pyActigraphy

This introduction notebook illustrates how to mask potentially spurious inactivity periods.

First, load the pyActigraphy package:

[1]:
import pyActigraphy

As well as your favourite plotting package:

[2]:
import plotly.graph_objs as go

Read an individual actigraphy file

As an example, let’s read a .awd file located in the test directory of the pyActigraphy package:

[3]:
import os
fpath = os.path.join(os.path.dirname(pyActigraphy.__file__),'tests/data/')
[4]:
raw = pyActigraphy.io.read_raw_awd(fpath+'example_01_mask.AWD')

Plot the data

[5]:
layout = go.Layout(title="Actigraphy data", xaxis=dict(title="Date time"), yaxis=dict(title="Counts/period"), showlegend=False)
[6]:
go.Figure(data=go.Scatter(x=raw.data.index.astype(str), y=raw.data), layout=layout)

At first sight, it seems that there is a suspicious period of total inactivity (activity counts equal to zero), starting at 9h30 on the 27th of January.

It seems spurious for several reasons:

  • it happens outside the usually sleep time window (although a nap might be possible…);

  • it consists of a long series of consecutive “total” inactivity (i.e. zero count) while other potential sleep periods do not;

  • it exhibits a sharp decrease of activity with respect to the surrounding time periods;

Most likely, it corresponds to a period where the actigraph has been removed by the participant.

Mask inactive data

While it is fairly (although not totally) simple to deal with inactivity periods at the beginning and at the end of the recordings (cf this other tutorial notebook), it is more difficult to deal, in an automatic fashion, with inactivity periods happening during the recording.

Fortunately, the pyActigraphy package offers a simple way to deal with these periods by masking the corresponding data.

Definition

Spurious inactivity periods are defined as series of consecutive zeros. The lenght of these series is configurable.

Create a mask automatically…

[7]:
raw.frequency
[7]:
Timedelta('0 days 00:01:00')
[8]:
# The duration corresponds either to the minimal number of inactive epochs (ex: duration=120)
# or the minimal length of the inactive period (duration='2h00min')
raw.create_inactivity_mask(duration='2h00min')
[9]:
raw.inactivity_length
[9]:
120

WARNING: Creating a mask does not mean it is applied. Cf. next section.**

Before applying it, let’s visualize it:

[10]:
layout = go.Layout(title="Data mask", xaxis=dict(title="Date time"), yaxis=dict(title="Mask"), showlegend=False)
[11]:
go.Figure(data=go.Scatter(x=raw.mask.index.astype(str),y=raw.mask),layout=layout)

Periods in the data for which the mask is equal to zero will be masked (once the mask is applied).

As expected, the spurious period of total inactivity of the 27th of January has been found by the pyActigraphy package and therefore can be masked upon request (cf. next section).

It is possible to dynamically change the configuration of the minimal duration. At each change, the mask is recomputed.

[12]:
raw.inactivity_length = '4h'
[13]:
go.Figure(data=go.Scatter(x=raw.mask.index.astype(str),y=raw.mask),layout=layout)

… or specify the periods manually.

First, let’s reset the mask by setting its length to None:

[14]:
# Reset mask
raw.inactivity_length = None
[15]:
raw.mask
/Users/ghammad/Work/pyActigraphy/pyActigraphy/io/base.py:179: UserWarning:

Inactivity length set to None. Could not create a mask.

add_mask_period

The function add_mask_period allows users to explicitly specify the period that should be masked.

If no mask has been created before, it is created on the fly when the first mask period is specified.

This function can be used as many times as needed.

[16]:
raw.add_mask_period(start='1918-01-27 09:30:00',stop='1918-01-27 17:48:00')

Let’s now inspect the newly created mask:

[17]:
go.Figure(data=go.Scatter(x=raw.mask.index.astype(str),y=raw.mask),layout=layout)

add_mask_periods

The add_mask_periods function allows users to use a file to specify several additional mask periods at once.

The file format accepted by pyActigraphy is the following:

Mask

Start_time

Stop_time

Remarks (optional)

mask_01

1918-02-01 20:42:00

1918-02-01 21:35:00

Reported in the sleep diary.

mask_02

1918-02-02 13:20:00

1918-02-02 14:14:00

Found by visual inspection.

[18]:
raw.add_mask_periods(os.path.join(os.path.dirname(pyActigraphy.__file__),'tests/data/example_masklog.csv'))
[19]:
go.Figure(data=go.Scatter(x=raw.mask.index.astype(str),y=raw.mask),layout=layout)

It can be seen here that two new periods were added to the mask, in addition to the period previously specified manually.

Apply the mask

Masking the spurious inactivity periods is crucial as it can substantially affect the calculation of the rest-activity rhythm related variables.

For example:

[20]:
raw.IS()
[20]:
0.44565929945478583

Applying the mask is controlled by the following variable:

[21]:
raw.mask_inactivity = True
[22]:
raw.IS()
[22]:
0.5097482433325218

Interesting, isn’t it?

Besides, the application of the masking is dynamic:

[23]:
raw.mask_inactivity = False
[24]:
raw.IS()
[24]:
0.44565929945478583

A last word; be cautious when masking your data. Series of consecutive periods of total inactivity might happen during sleep… A visual check of the mask is always a good idea.