Introduction to the “light” exposure data module of pyActigraphy

There shall be light!

There shall be light!

Photo by Jonathan Borba on Unsplash

Disclaimer

The development of the pyActigraphy module for analysing light exposure data was led and financially supported by members of the Daylight Academy Project The role of daylight for humans (led by Mirjam Münch, Manuel Spitschan). The module is part of the Human Light Exposure Database. For more information about the project, please see https://daylight.academy/projects/state-of-light-in-humans/.

Introduction

Many actigraphy devices also record light exposure data. As light is a strong zeitgeber, it is likely that any circadian analysis, if light itself is not its primary focus, will at least control for interindividual differences in light exposure.

This is where the “light” module of pyActigraphy comes into play. It provides access to light exposure data recorded by several devices and light-specific metrics.

This first introduction tutorial presents how to:

  • access light exposure data from various actigraphy devices

  • visualize light exposure data using the plotly package

NB(1): there are many visualization packages available in Python (matplotlib, seaborn, plotly, etc). Feel free to use the one you like the most. Plotly is used here for convenience as it is part of pyActigraphy dependencies and thus automatically installed when installing pyActigraphy.

NB(2): by default, the light exposure data are automatically log-transformed (log10+1) upon reading the recording with pyActigraphy.

How to get some help?

In pyActigraphy, the inline documentation of a variable or a function can easily be access through the help command.

Imports and input data

First, let’s import the necessary packages:

[1]:
import pyActigraphy
[2]:
import plotly.graph_objects as go
[3]:
import os

Now, in the context of this tutorial, we will use as input data a sample file recorded by a ActTrust device (Condor Instrument), located in the test directory of the pyActigraphy package itself.

[4]:
fpath = os.path.join(
    os.path.dirname(pyActigraphy.__file__),
    'tests','data',
    'test_sample_atr.txt'
)

Reading such a file with pyActigraphy is easy:

[5]:
raw = pyActigraphy.io.read_raw_atr(fpath)

For more information about the various file formats that can be read by pyActigraphy or about the information that can be retrieved via this raw object, please see this tutorial.

How to access light exposure data

Light exposure data can be accessed through the light attribute of the rawobject:

[6]:
raw.light
[6]:
<pyActigraphy.light.light.LightRecording at 0x155a78550>

This command returns an object (LightRecording) that both contains the light exposure data and gives you access to various light-specific metrics. More information about the `LightRecording <https://ghammad.github.io/pyActigraphy/LightRecording.html>`__ class.

Some devices record light data via different sensors, providing thus multiple channels. To see the list of available channels:

[7]:
raw.light.get_channel_list()
[7]:
Index(['LIGHT', 'AMB LIGHT', 'RED LIGHT', 'GREEN LIGHT', 'BLUE LIGHT',
       'IR LIGHT', 'UVA LIGHT', 'UVB LIGHT'],
      dtype='object')

In this example case, multiple light channels are available. To access the light data, it is possible to either get a single channel directly:

[8]:
raw.light.get_channel('RED LIGHT')
[8]:
DATE/TIME
1918-01-01 09:00:00    1.719000
1918-01-01 09:01:00    1.643650
1918-01-01 09:02:00    1.630530
1918-01-01 09:03:00    1.625312
1918-01-01 09:04:00    1.619406
                         ...
1918-01-05 08:55:00    0.000000
1918-01-05 08:56:00    0.000000
1918-01-05 08:57:00    0.000000
1918-01-05 08:58:00    0.000000
1918-01-05 08:59:00    0.000000
Freq: T, Name: RED LIGHT, Length: 5760, dtype: float64

Or get multiple channels at once:

[9]:
raw.light.get_channels(['RED LIGHT', 'GREEN LIGHT', 'BLUE LIGHT'])
[9]:
RED LIGHT GREEN LIGHT BLUE LIGHT
DATE/TIME
1918-01-01 09:00:00 1.719000 1.874424 1.590953
1918-01-01 09:01:00 1.643650 1.803730 1.516932
1918-01-01 09:02:00 1.630530 1.802500 1.514415
1918-01-01 09:03:00 1.625312 1.790074 1.495544
1918-01-01 09:04:00 1.619406 1.786822 1.493319
... ... ... ...
1918-01-05 08:55:00 0.000000 0.000000 0.000000
1918-01-05 08:56:00 0.000000 0.000000 0.004321
1918-01-05 08:57:00 0.000000 0.004321 0.004321
1918-01-05 08:58:00 0.000000 0.004321 0.004321
1918-01-05 08:59:00 0.000000 0.004321 0.004321

5760 rows × 3 columns

Or access the underlying data (pandas.DataFrame) and select the requested channel:

[10]:
raw.light.data.loc[:,['UVA LIGHT','UVB LIGHT']]
[10]:
UVA LIGHT UVB LIGHT
DATE/TIME
1918-01-01 09:00:00 0.0 0.0
1918-01-01 09:01:00 0.0 0.0
1918-01-01 09:02:00 0.0 0.0
1918-01-01 09:03:00 0.0 0.0
1918-01-01 09:04:00 0.0 0.0
... ... ...
1918-01-05 08:55:00 0.0 0.0
1918-01-05 08:56:00 0.0 0.0
1918-01-05 08:57:00 0.0 0.0
1918-01-05 08:58:00 0.0 0.0
1918-01-05 08:59:00 0.0 0.0

5760 rows × 2 columns

NB: remember that, in both cases, the light exposure data are automatically log-transformed (log10+1).

How to visualize light exposure data

In this tutorial, the python package “plotly” is used to display graphics. However, feel free to use your favourite graphic library.

First, let’s create the graphic layout:

[11]:
layout = go.Layout(
    title="Light exposure data",
    xaxis=dict(title="Date time"),
    yaxis=dict(title="$log_{10}(\mathrm{Light~intensity})+1~\mathrm{[microwatt/cm^2]}$"),
    showlegend=False
)

And plot the (red) light data:

[12]:
fig1 = go.Figure(
    data=[go.Scatter(
        x=raw.light.get_channel('RED LIGHT').index.astype(str),
        y=raw.light.get_channel('RED LIGHT'),
        name='Red light')
    ],
    layout=layout
)
[13]:
fig1.show()

Since all the recorded light channels are readily available, displaying multiple channels is easy:

[14]:
layout.update(showlegend=True);
[15]:
fig2 = go.Figure(
    data=[
        go.Scatter(
            x=raw.light.get_channel('RED LIGHT').index.astype(str),
            y=raw.light.get_channel('RED LIGHT'),
            name='Red light',
            line={'color':'red'}
        ),
        go.Scatter(
            x=raw.light.get_channel('BLUE LIGHT').index.astype(str),
            y=raw.light.get_channel('BLUE LIGHT'),
            opacity=.75,
            name='Blue light',
            line={'color':'blue'}
        ),
        go.Scatter(
            x=raw.light.get_channel('GREEN LIGHT').index.astype(str),
            y=raw.light.get_channel('GREEN LIGHT'),
            opacity=.5,
            name='Green light',
            line={'color':'green'}
        )
    ],
    layout=layout
)
[16]:
fig2.show()

Et voilà! For now…