Chronobiology

A python package to calculate and plot circadian cycles data.

Introduction

_images/periodogram.png

Circadian rhythms are ~24 hour cycles of physiology and behaviour that occur in virtually all organisms from bacteria to man. These rhythms are generated by an internal biological clock and persist even in isolation from any external environmental cues. In humans, circadian rhythms in activity are typically measured using calibrated wrist-worn accelerometers. By contrast, the activity of laboratory animals is typically measured using home cage running wheels. Circadian data are typically double plotted as actograms, showing activity across multiple days.

The circadian field has developed standard methods for analysing circadian rhythms. This primarily includes methods to detect recurring features in the data, enabling the period length of activity cycles to be determined. Under entrained conditions, this period will normally be determined by environmental zeitgebers A range of different methods are used to determine the underlying period in biological time series. Three of the most commonly used are the Enright periodogram, Fourier analysis and the Lomb-Scargle periodogram. In addition, activity onset is also frequently used to characterise phase shifts in rhythms in response to environmental zeitgebers.

Circadian disruption may occur as a result of environmental conditions. This includes misalignment (when two or more rhythms adopt an abnormal phase relationship) and desynchrony (when two or more rhythms exhibit a different period). A range of approaches have been used to assess circadian disruption. These methods range from simple visual inspection of actograms to metrics such as periodogram power, variability in activity onset, light phase activity, activity bouts, interdaily stability, intradaily variability and relative amplitude.

This package provides a set of tools to calculate and plot these parameters based on activity measurements for further inspection and analysis.

For more theory see the following paper:

Telling the Time with a Broken Clock: Quantifying Circadian Disruption in Animal Models

Installation

Chronobiology can be installed using pip:

$ pip install chronobiology

Storing data

Activity data collected during an experiment is usually represented by an array of records with the following data:

  • time of measurement;

  • measured activity value;

  • time of day marker (night or day).

We recommend InfluxDB to store activity data.

Only InfluxDB v1.x is supported, don’t use InfluxDB v2.x.

Each searies of measurements should be represented by a separate InfluxDB table with the following fields:

  • value – numeric values obtained by sensors;

  • is_night – day/night marker (True for nighttime measurements and False for daytime measurements).

Other data (like sensor_id in case of multiple sensors) can also be included in the table as tags.

In InfluxDB, data can be stored as a field or tag. Tags are more efficient to construct WHERE-queries but they can hold only string data. So it’s better to have value and is_night stored as fields and other data (required for filtering purpuses) stored as tags.

For installing and configuring InfluxDB see the official documentation. Once your InfluxDB is initialized and configured, you are ready to write data to it.

Writing data to InfluxDB using Python

In the following example, random measurements are generated and written to database via InfluxDBClient which can be downloaded using pip:

$ pip install influxdb
[1]:
from influxdb import InfluxDBClient
import json
from datetime import datetime, timedelta
import random
[2]:
# Create a client to access the database.
client = InfluxDBClient('localhost', 8086, 'admin', 'pass', database='my_db')
[3]:
# Generate random measurements.
measurements=[]
for i in range(5):
    measurements.append({
        'measurement': 'test_series',
        'tags': {
            'sensor_id': '1',
        },
        'time': datetime.fromisoformat('2021-01-01') + timedelta(hours=i),
        'fields': {
            'value': random.uniform(1, 10),
            'is_night': True,
        }
    })
measurements
[3]:
[{'measurement': 'test_series',
  'tags': {'sensor_id': '1'},
  'time': datetime.datetime(2021, 1, 1, 0, 0),
  'fields': {'value': 2.636775703935856, 'is_night': True}},
 {'measurement': 'test_series',
  'tags': {'sensor_id': '1'},
  'time': datetime.datetime(2021, 1, 1, 1, 0),
  'fields': {'value': 9.634964674586048, 'is_night': True}},
 {'measurement': 'test_series',
  'tags': {'sensor_id': '1'},
  'time': datetime.datetime(2021, 1, 1, 2, 0),
  'fields': {'value': 5.488104584244404, 'is_night': True}},
 {'measurement': 'test_series',
  'tags': {'sensor_id': '1'},
  'time': datetime.datetime(2021, 1, 1, 3, 0),
  'fields': {'value': 2.3051122236289237, 'is_night': True}},
 {'measurement': 'test_series',
  'tags': {'sensor_id': '1'},
  'time': datetime.datetime(2021, 1, 1, 4, 0),
  'fields': {'value': 5.834270867285628, 'is_night': True}}]
[4]:
# Insert generated data to the database.
client.write_points(measurements);
[5]:
# Show the inserted data.
client.query('''SELECT (*) FROM "test_series"''')
[5]:
ResultSet({'('test_series', None)': [{'time': '2021-01-01T00:00:00Z', 'is_night': True, 'sensor_id': '1', 'value': 2.636775703935856}, {'time': '2021-01-01T01:00:00Z', 'is_night': True, 'sensor_id': '1', 'value': 9.634964674586048}, {'time': '2021-01-01T02:00:00Z', 'is_night': True, 'sensor_id': '1', 'value': 5.488104584244404}, {'time': '2021-01-01T03:00:00Z', 'is_night': True, 'sensor_id': '1', 'value': 2.3051122236289237}, {'time': '2021-01-01T04:00:00Z', 'is_night': True, 'sensor_id': '1', 'value': 5.834270867285628}]})

Selecting data

Once your data is written to InfluxDB, you are ready to select it for analysis.

This package provides the DBQuery class to connect to your InfluxDB and read data from it. Here is an example of it’s usage.

[1]:
from chronobiology.chronobiology import DBQuery
[2]:
# Create a client to access a local database.
client = DBQuery('my_db', 'admin', 'pass')

Getting measurements

[3]:
# Get all measurements found in database.
client.get_measurements()
[3]:
['test_series']

Getting tags

[4]:
# Get tags of a series.
client.get_tags('test_series')
[4]:
['sensor_id']
[5]:
# Try to get tags of a nonexistent series.
client.get_tags('foo')
[5]:
[]

Getting fields

[6]:
# Get fields of a series.
client.get_fields('test_series')
[6]:
['is_night', 'value']
[7]:
# Get fields of a series with their types.
client.get_fields('test_series', return_types=True)
[7]:
(['is_night', 'value'], ['boolean', 'float'])
[8]:
# Try to get fields of a nonexistent series.
client.get_fields('foo')
[8]:
[]

Getting keys

Keys is a set of all possible values of a tag.

[9]:
# Get keys of a series tag.
client.get_keys('test_series', 'sensor_id')
[9]:
['1']
[10]:
# Try to get keys of a nonexistent series.
client.get_keys('foo', 'sensor_id')
[10]:
[]
[11]:
# Try to get keys of a nonexistent tag.
client.get_keys('test_series', 'foo')
[11]:
[]

Getting data

Data includes both fields and tags.

[12]:
# Get all data from a series.
client.get_data('test_series', '*')
[12]:
{'time': array(['2021-01-01T00:00:00.000000000', '2021-01-01T01:00:00.000000000',
        '2021-01-01T02:00:00.000000000', '2021-01-01T03:00:00.000000000',
        '2021-01-01T04:00:00.000000000'], dtype='datetime64[ns]'),
 'is_night': array([ True,  True,  True,  True,  True]),
 'value': array([2.6367757 , 9.63496467, 5.48810458, 2.30511222, 5.83427087]),
 'sensor_id': array(['1', '1', '1', '1', '1'], dtype='<U1')}
[13]:
# Get values of a single field of a series.
client.get_data('test_series', 'value')
[13]:
{'time': array(['2021-01-01T00:00:00.000000000', '2021-01-01T01:00:00.000000000',
        '2021-01-01T02:00:00.000000000', '2021-01-01T03:00:00.000000000',
        '2021-01-01T04:00:00.000000000'], dtype='datetime64[ns]'),
 'value': array([2.6367757 , 9.63496467, 5.48810458, 2.30511222, 5.83427087])}
[14]:
# Get values of multiple fields/tags of a series.
client.get_data('test_series', ['value', 'sensor_id'])
[14]:
{'time': array(['2021-01-01T00:00:00.000000000', '2021-01-01T01:00:00.000000000',
        '2021-01-01T02:00:00.000000000', '2021-01-01T03:00:00.000000000',
        '2021-01-01T04:00:00.000000000'], dtype='datetime64[ns]'),
 'value': array([2.6367757 , 9.63496467, 5.48810458, 2.30511222, 5.83427087]),
 'sensor_id': array(['1', '1', '1', '1', '1'], dtype='<U1')}
[15]:
# Get data filtered by a single key value.
client.get_data('test_series', '*', keys={'sensor_id': '1'})
[15]:
{'time': array(['2021-01-01T00:00:00.000000000', '2021-01-01T01:00:00.000000000',
        '2021-01-01T02:00:00.000000000', '2021-01-01T03:00:00.000000000',
        '2021-01-01T04:00:00.000000000'], dtype='datetime64[ns]'),
 'is_night': array([ True,  True,  True,  True,  True]),
 'value': array([2.6367757 , 9.63496467, 5.48810458, 2.30511222, 5.83427087]),
 'sensor_id': array(['1', '1', '1', '1', '1'], dtype='<U1')}
[16]:
# Get data filtered by multiple key values.
client.get_data('test_series', '*', keys={'sensor_id': ['1', '2']})
[16]:
{'time': array(['2021-01-01T00:00:00.000000000', '2021-01-01T01:00:00.000000000',
        '2021-01-01T02:00:00.000000000', '2021-01-01T03:00:00.000000000',
        '2021-01-01T04:00:00.000000000'], dtype='datetime64[ns]'),
 'is_night': array([ True,  True,  True,  True,  True]),
 'value': array([2.6367757 , 9.63496467, 5.48810458, 2.30511222, 5.83427087]),
 'sensor_id': array(['1', '1', '1', '1', '1'], dtype='<U1')}
[17]:
# Get data newer than a given timestamp (inclusive).
client.get_data('test_series', '*', keys={'sensor_id': '1'}, start='2021-01-01 02:00:00')
[17]:
{'time': array(['2021-01-01T02:00:00.000000000', '2021-01-01T03:00:00.000000000',
        '2021-01-01T04:00:00.000000000'], dtype='datetime64[ns]'),
 'is_night': array([ True,  True,  True]),
 'value': array([5.48810458, 2.30511222, 5.83427087]),
 'sensor_id': array(['1', '1', '1'], dtype='<U1')}
[18]:
# Get data older than a given timestamp (exclusive).
client.get_data('test_series', '*', keys={'sensor_id': '1'}, stop='2021-01-01 03:00:00')
[18]:
{'time': array(['2021-01-01T00:00:00.000000000', '2021-01-01T01:00:00.000000000',
        '2021-01-01T02:00:00.000000000'], dtype='datetime64[ns]'),
 'is_night': array([ True,  True,  True]),
 'value': array([2.6367757 , 9.63496467, 5.48810458]),
 'sensor_id': array(['1', '1', '1'], dtype='<U1')}

Analyzing data

Class CycleAnalyzer is used for calculating and plotting circadian cycles data.

For more info about the analysis methods, see the paper.

Here’s an example of analyzing randomly generated activity data.

[1]:
import numpy as np
from chronobiology.chronobiology import CycleAnalyzer, generate_data
[2]:
# Generate random activity data.
data = generate_data()

# Prepare input arrays for CycleAnalyzer.
# When working with InfluxDB, same arrays must be selected using a DBQuery instance.
timestamps = data['time'] # timestamps of measurements
values = data['value']    # activity values
nights = data['is_night'] # night activity markers

print(f'{len(timestamps)} measurements generated.')
np.set_printoptions(threshold=10)
data
870 measurements generated.
[2]:
{'time': array(['2020-01-01T00:03:00.000000000', '2020-01-01T03:29:00.000000000',
        '2020-01-01T03:53:00.000000000', ...,
        '2020-01-10T20:42:00.000000000', '2020-01-10T22:02:00.000000000',
        '2020-01-10T22:47:00.000000000'], dtype='datetime64[ns]'),
 'value': array([1, 1, 1, ..., 1, 1, 1]),
 'is_night': array([False,  True,  True, ...,  True,  True,  True])}
[3]:
# Create a cycle analyzer.
ca = CycleAnalyzer(timestamps, values, nights)

Actogram

The most obvious way of assessing circadian disruption is to simply look at experimental actograms. This visual inspection should always take the form of circadian disruption into account, and whether this is environmental or genetic in origin. This will also determine the appropriate controls, be they baseline conditions or wildtype littermates, respectively. Furthermore, when studying circadian disruption under different lighting paradigms, comparing activity relative to the external LD cycle is critical. This initial visual inspection of actogram data will guide the choice of which additional measurements are likely to be most informative.

[4]:
ca.plot_actogram()
_images/analyzing_data_5_0.png

Periodogram

The power of a periodogram provides a measure of the strength and regularity of the underlying rhythm, with higher values indicating robust rhythms. In circadian disruption — where rhythms are typically less robust — periodogram power is expected to be reduced and low values may indicate the absence of a significant circadian rhythm. The power of the chi-square periodogram (\(Q_p\)) has been widely used as a measure of the robustness of circadian rhythms, and can be traced back to studies on the effects of constant light on the strength of activity and sleep rhythms in rats. Analysis of \(Q_p\) based upon simulated and empirical data sets has shown that this provides a valuable measure of the robustness of circadian rhythms. Periodogram analysis is particularly informative in internal desynchrony, where the power of multiple periodicities within a dataset will be evident.

[5]:
ca.plot_periodogram()
_images/analyzing_data_7_0.png

Activity oneset

A hallmark of normal circadian function is that activity onset is consistent from day to day. In most records, the onset of activity is typically a more precise phase marker than the offset of activity. As such, the variability in activity onset across multiple days – either relative to the light/dark cycle (phase angle of entrainment) or under constant conditions – provides a useful metric to describe the precision of circadian rhythms. Phase angle of entrainment and the variability in activity onset have been widely used in the study of circadian entrainment. Activity onset is particularly informative when studying circadian misalignment and chronodisruption, where the phasing of activity with regard to environmental zeitgebers is expected to differ.

[6]:
ca.activity_onset()
[6]:
array(['2020-01-01T03:29', '2020-01-02T02:45', '2020-01-03T02:37',
       '2020-01-04T02:43', '2020-01-05T03:11', '2020-01-06T01:59',
       '2020-01-07T03:43', '2020-01-08T02:20', '2020-01-09T01:50',
       '2020-01-10T02:02'], dtype='datetime64[m]')
[7]:
ca.plot_activity_onset()
_images/analyzing_data_10_0.png

Light phase activity

In nocturnal species, such as laboratory mice, activity is normally confined to the dark phase of the light/dark cycle. A hallmark of disrupted rhythms is therefore an increased activity in the normally inactive light phase, and such changes are expected to occur in both circadian misalignment and chronodisruption. In diurnal species, dark phase activity can similarly be used to quantify the amount of activity occurring in the normal inactive phase. Light phase activity has been widely used to study defects in circadian entrainment to light as well as in disease-relevant models. Light phase activity also provides an ideal measure to assess the impact of misaligned feeding, a specific example of circadian misalignment.

[8]:
ca.light_activity(auc=True)
[8]:
(array([0.0952381 , 0.05813953, 0.03529412, 0.04545455, 0.08045977,
        0.04705882, 0.05617978, 0.02272727, 0.03370787, 0.02247191]),
 0.04942528735632184,
 0.4978093187109366)
[9]:
ca.plot_light_activity()
_images/analyzing_data_13_0.png

Activity bouts

As a result of less consolidated activity and rest phases, circadian disruption is associated with an increased number and reduced duration of activity bouts. The number and duration of activity bouts are frequently used as a measure of fragmentation in circadian disruption. Due to the inappropriate phasing of activity/rest cycles with regard to the external environmental, circadian misalignment, internal desynchrony and chronodisruption are all expected to affect activity bouts.

[10]:
ca.daily_bouts()
[10]:
(array([57, 67, 62, 68, 64, 67, 63, 66, 68, 62]),
 array([1.47368421, 1.28358209, 1.37096774, 1.29411765, 1.359375  ,
        1.26865672, 1.41269841, 1.33333333, 1.30882353, 1.41935484]))
[11]:
ca.plot_daily_bouts()
_images/analyzing_data_16_0.png
[12]:
ca.plot_bout_histogram()
_images/analyzing_data_17_0.png

Inter-daily stability

Inter-daily stability (IS) measures the day-to-day reproducibility of rest/activity cycles. Patterns of activity are typically reproducible in healthy individuals, whereas circadian disruption results in more variable rhythms. IS was first described in the study of elderly human patients with Alzheimer’s disease. It has subsequently been widely used in a number of human studies, and has also been adopted for studying circadian disruption in animal models. Due to the day-to-day changes in the relationship between circadian and environmental time, decreased IS may be expected in internal desynchrony. However, circadian misalignment and chronodisruption may not influence IS if a stable new phase-relationship is established.

[13]:
ca.interdaily_stability()
[13]:
0.9509106984969055

Intra-daily variability

Intra-daily variability (IV) is a measure of the fragmentation of activity rhythms. First introduced for the study of patients with Alzheimer’s disease, like IS it has been readily adopted for the study of animal models of circadian disruption. IV measures the frequency of transitions between activity and rest across the day. More transitions between activity and rest result in higher IV scores. As with activity bouts, circadian misalignment, internal desynchrony and chronodisruption may all increase IV due to the inappropriate phasing of activity/rest cycles with regard to the external environment. A good example of this occurs in aging, where IV increases steadily with age in mice.

[14]:
ca.intradaily_variability()
[14]:
(array([0.67764081, 0.44996891, 0.46650498, 0.50534161, 0.53465811,
        0.57466193, 0.62592347, 0.44469921, 0.53629512, 0.64607892]),
 0.5521986259617444)
[15]:
ca.plot_intradaily_variability()
_images/analyzing_data_22_0.png

Relative amplitude

Perhaps the most obvious measure of the strength of any biological rhythm is its amplitude. A simple metric that has been widely used in human studies is relative amplitude (RA), again originating from studies in patients with Alzheimer’s disease. RA is a simple measure of the difference between periods of peak activity and rest. As healthy rhythms are assumed to display consolidated activity and rest periods, RA is expected to be reduced when normal circadian rhythms are disrupted. Both circadian misalignment and chronodisruption will reduce RA, with internal desynchrony potentially resulting in RA values that fluctuate as periods move in and out of phase.

[16]:
ca.relative_amplitude(auc=True)
[16]:
(array([0.89333333, 0.94871795, 0.94736842, 0.90123457, 0.85365854,
        0.8974359 , 0.85365854, 0.90243902, 0.95121951, 0.85542169]),
 0.8998748435544431,
 8.998188196576098)
[17]:
ca.plot_relative_amplitude()
_images/analyzing_data_25_0.png

Chronobiology module

Main module providing classes and methods to collect, generate, calculate and plot circadian cycles data.

Classes

DBQuery

Class to access InfluxDB 1.x and select records from it.

CycleAnalyzer

Class to calculate and plot circadian cycles data.

Methods

generate_data

Generate random input data.

generate_night

Generate a random is_night array.

class chronobiology.chronobiology.DBQuery(database, username, password, host='localhost', port=8086)

Class to access InfluxDB 1.x and select records from it.

Parameters
  • database (str) – Name of the database.

  • username (str) – Name of user.

  • password (str) – User password.

  • host (str, optional) – IP adress, defaults to localhost.

  • port (int, optional) – Connection port, defaults to 8086.

Methods

get_measurements

Get list of all measurments (series) in the database.

get_tags

Get all tags (tag names) in a series.

get_fields

Get all fields in a series.

get_keys

Get list of all tag values for a given tag in a series.

get_data

Get data (records) for specified fields/tags in a series.

get_measurements()

Get list of all measurments (series) in the database.

Return type

list[str]

Returns

List of all measurement names in the database.

get_tags(series)

Get all tags (tag names) in a series.

Parameters

series (str) – Name of the series.

Return type

list[str]

Returns

List of all tag names in the series.

Note

Returns an empty list if the query does not return any vaues, for example, if there are no tags in the series or if there is no series with the given name.

get_fields(series, return_types=False)

Get all fields in a series.

Parameters
  • series (str) – Name of the series.

  • return_types (bool, optional) – Indicates if field types should be returned, defaults to False.

Return type

list[str]|(list[str], list[str])

Returns

If return_type == False

List of field names.

If return_type == True

Field names and the corresponding InfluxDB field types as a pair of lists of strings. The possible types are: integer, float, string and boolean.

Note

Returns an empty list if the query does not return any vaues, for example, if there are no tags in the series or if there is no series with the given name.

get_keys(series, tag)

Get list of all tag values for a given tag in a series.

Parameters
  • series (str) – Name of the series.

  • tag (str) – Name of the tag.

Return type

list[str]

Returns

List of all values of the tag in the series.

Note

Returns an empty list if the query does not return any vaues, for example, if there are no tags in the series or if there is no series with the given name.

get_data(series, fields, keys=None, start=None, stop=None, local_tz=False)

Get data (records) for specified fields/tags in a series.

Parameters
  • series (str) – Name of the series.

  • fields (str|list[str]|tuple[str]|set[str]|dict[str: str|type]) –

    Name(s) of fields/tags in the series. This parameter is treated differently depending on it’s type:

    str

    Treated as a single field/tag name to return. If fields = '*' then all fields and tags are returned.

    list[str], tuple[str] or set[str]

    Treated as a collection of field/tag names to return.

    dict[str: str|type]

    The keys are treated as field/tag names, and the values are treated as numpy types (or names of numpy types) of the corresponding keys.

    The output is converted from InfluxDB types to the types specified in the dictionary.

    Use None as a field type to enable type autodetection and/or avoid type conversion for that field.

  • keys (None|dict[str: obj], optional) –

    Dictionary providing rules to select records with specific field/tag values, defaults to None.

    If None then selected records are not filtered. Otherwise the dictionary is treated as follows:

    Key

    Name of the filtered field/tag.

    Values

    Value(s) of the corresponding field/tag to be selected.

    Each value can be a scalar or a collection of all values to be selected (list, tuple or set)

  • start (None|str|int|datetime, optional) –

    Inclusive lower time boundary for the returned data, defaults to None.

    None indicates no lower boundary.

    str is interpreted as a timestring.

    int is interpreted as a Unix timestamp.

    datetime is used as is.

  • stop (None|str|int|datetime, optional) –

    Exclusive upper time boundary for the returned data, defaults to None.

    None indicates no upper boundary.

    str is interpreted as a timestring.

    int is interpreted as a Unix timestamp.

    datetime is used as is.

  • local_tz (bool, optional) – Indicates whether local or UTC time is used in the code, defaults to False (UTC).

Return type

dict[str: np.array]

Returns

Dictionary constructed as follows:

Key

Field/tag name.

Value

Numpy array of the corresponding field/tag values.

class chronobiology.chronobiology.CycleAnalyzer(timestamps, activity=None, night=None, step='1m', start=None, stop=None, descr='', max_gap='1m', min_duration='1m', min_activity=1, min_data_points=1)

Class to calculate and plot circadian cycles data.

Variables
  • start (np.datetime64) – Adjusted lower time boundary (inclusive) of data included in analysis. Due to start time adjustment it might differ from 00:00:00 but it’s guaranteed that self.start belongs to the same day (date) as the start argument passed during the class initialization (or that it falls in the same day as the first timestamp in the timestamps argument if start is None).

  • stop (np.datetime64) – Adjusted upper time boundary (exclusive) of data included in analysis. Due to start time adjustion it might differ from 00:00:00 but it’s guaranteed that self.stop is no less than the stop argument passed during the class initialization and that self.stop - self.start is equal to the whole number of days.

  • step (np.timedelta64) – Same as the constructor argument.

  • steps_per_day (int) – Numbers of steps (bins) in a day.

  • descr (str) – Same as the constructor argument.

  • total_days (int) – Total number of days (including filtered out) between the start and stop timestamps.

  • days (int) – Number of days not filtered out due to low activity.

  • vartype – Mask calculated from data and min_data_points. Array of length equal to total_days. Holds True for the days that weren’t filtered out due to low activity and False for those that were.

  • min_data_points (int) – Same as the constructor argument.

  • max_gap (np.timedelta64) – Same as the constructor argument, but converted to np.timedelta64[m] (minute precision).

  • min_duration (np.timedelta64) – Same as the constructor argument, but converted to np.timedelta64[m] (minute precision).

  • min_activity (int) – Same as the constructor argument.

Parameters
  • timestamps (np.array[np.timedelta64]) – Timestamps of activity records.

  • activity (None|np.array[int|float], optional) – Activity events (weights) associated with each timestamp. If not specified then it’s assumed that activity = 1 for each timestamp.

  • night (None|np.array[bool], optional) –

    Boolean array denoting timestamps associated with night. The i-th timestamp is associated with night if night[i] = True and with day otherwise. If not specified then it’s assumed that night = True for each timestamp.

    Note

    When binning, if there are conflicting values for night in a bin (timestamps with both night = True and night = False fall into the same bin) then night = True is written for that bin (i.e. True values override False values in the same bin).

  • step (str|int|timedelta, optional) – Discretization step, defaults to '1m'. All data is discretized by binning it into bins of this size.

  • start (None|str|int|datetime, optional) – Lower time boundary (inclusive) for processed records, defaults to None. None indicates no lower boundary.

  • stop (None|str|int|datetime, optional) – Upper time boundary (exclusive) for processed records, defaults to None. None indicates no upper boundary.

  • descr (str, optional) – Textual description appended to the end of plot headers, defaults to ''. Used to specify the source of data on the plots.

  • max_gap (str|int|timedelta, optional) – Maximal gap between two consecutive activity events, defaults to '1m'. Consecutive events with distance between them larger than max_gap belong to different activity bouts.

  • min_duration (str|int|timedelta, optional) – Minimal duration of activity bout, defaults to '1m'. Activity bouts with shorter duration (defined as the time difference between the last activity event in a bout and the first one) are discarded.

  • min_activity (int, optional) – Minimal value of (binned) activity, defaults to 1. Bins with activity less than min_activity are discarded during activity bout calculations. For example, if self.step = '5 min' and min_activity = 6 then the data is binned into 5 minute bins, and during the activity bout calculation, bins with activity < 6 are treated as if there is no activity within their intervals.

  • min_data_points (int, optional) – Minimum number of data points (records) in a day, defaults to 1. Days with fewer data points (records) are filtered out.

Properties

timestamps

Timestamps of activity records.

activity

Activity events associated with each timestamp.

night

Boolean array denoting the timestamps associated with night.

bouts

Precalculated bouts to be used in class methods when bouts=True is passed to them.

Methods

activity_bouts

Calculate activity bouts using provided parameters.

update_bouts

Re-calculate and update self.bouts using provided parameters.

filter_inactive

Re-filter days based on a new minimal daily number of data points.

plot_actogram

Plot double actogram with right half shifted by 1 day.

periodogram

Calculate periodogram for the range of periods.

plot_periodogram

Calculate and plot periodogram() for the range of periods.

light_activity

Calculate light phase activity.

plot_light_activity

Calculate and plot light_activity() for each day.

interdaily_stability

Calculate interdaily stability.

intradaily_variability

Calculate intradaily variability.

plot_intradaily_variability

Calculate and plot intradaily_variability() for each day.

relative_amplitude

Calculate relative amplitude.

plot_relative_amplitude

Calculate and plot relative_amplitude() for each day.

daily_bouts

Calculate daily activity bout statistics.

plot_daily_bouts

Calculate and plot daily_bouts() statistics.

plot_bout_histogram

Plot histogram of activity_bouts() duration distribution.

activity_onset

Calculate activity onset for each day.

plot_activity_onset

Calculate and plot activity_onset() for each day.

Note

Most methods can base their analysis on either number of activity events or precalculated activity bouts.

Note

Methods accepting timespan parameters (e.g. start, min_duration and all other parameters of type str|int|timedelta) can treet them differently depending on their type:

str parameters are parsed like timedelta strings (e.g. '03:43:40'),

int parameters are treated as the number of nanoseconds,

timedelta parameters are treated as they are.

property timestamps

Timestamps of activity records.

Type

np.array[np.datetime64]

Returns

Bin starts (discrete time points). Days with number of data points less than min_data_points are not included.

property activity

Activity events associated with each timestamp.

Type

np.array[int|float]

Returns

Activity values for each bin. Days with the number of data points less than min_data_points are not included.

Activity value for each bin is equal to the sum of activity values for input data point that fall into this bin.

property night

Boolean array denoting the timestamps associated with night.

The i-th timestamp is associated with night if night[i] = True and with day otherwise.

Type

np.array[bool]

Returns

Filtered night values for each bin. Days with the number of data points less than min_data_points are not included.

property bouts

Precalculated bouts to be used in class methods when bouts=True is passed to them.

Type

np.array[int]

Returns

Bouts list.

activity_bouts(max_gap=None, min_duration=None, min_activity=None)

Calculate activity bouts using provided parameters.

Discretization step is taken from the constructor (self.step).

Warning

Do not update self.bouts manually, call update_bouts() instead.

Parameters
  • max_gap (None|str|int|timedelta, optional) – Overrides self.max_gap if specified.

  • min_duration (None|str|int|timedelta, optional) – Overrides self.min_duration if specified.

  • min_activity (None|int, optional) – Overrides self.min_activity if specified.

Return type

np.array[int]

Returns

Array holding 1 for steps that belong to some activity bout and 0 otherwise. Consecutive 1’s in bouts belong to the same bout.

update_bouts(max_gap=None, min_duration=None, min_activity=None)

Re-calculate and update self.bouts using provided parameters.

Also updates max_gap, min_duration and min_activity if they are not None.

Discretization step is taken from class initialization time (self.step).

Parameters
  • max_gap (None|str|int|timedelta, optional) – Overrides self.max_gap if specified.

  • min_duration (None|str|int|timedelta, optional) – Overrides self.min_duration if specified.

  • min_activity (None|int, optional) – Overrides self.min_activity if specified.

filter_inactive(min_data_points=1)

Re-filter days based on a new minimal daily number of data points.

Parameters

min_data_points (int, optional) – Minimum number of data points (records) in a day, defaults to 1. Days with fewer data points (records) are filtered out.

plot_actogram(step=None, bouts=False, log=False, activity_onset='step', percentile=20, N='6h', M='6h', filename=None, width=1000, height=100, dpi=100)

Plot double actogram with right half shifted by 1 day.

Night intervals for each day are also plotted on actogram as well as activity onset for each day.

Parameters
  • step (None|str|int|timedelta, optional) – Bin size (the number of bars in actogram is equal to [24 hours] / step), defaults to None. None stands for self.max_gap.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • log (bool, optional) – Indicates whether the log of activity is plotted, defaults to False. Has no effect if bouts = True.

  • activity_onset (None|False|str, optional) – Shape of the convolution kernel to use for activity onset calculation, defaults to 'step'. To turn the activity onset display off pass None, False or string 'none' (case-insensitive). Otherwise it must be one of the strings recognized by self.activity_onset().

  • percentile (int, optional) – Percentile of the daily non-zero activity to use as a threshold for activity onset calculation, defaults to 20. Activity lower than that is treated as a zero activity.

  • N (str|int|timedelta, optional) – Length of the period of negative values (daytime, left part) in the convolution kernel for activity onset calculation, defaults to '6h'.

  • M (str|int|timedelta, optional) – Length of the period of positive values (nighttime, right part) in the convolution kernel for activity onset calculation, defaults to '6h'.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 100.

  • dpi (int, optional) – Plot resolution, defaults to 100.

periodogram(step=None, min_period='16h', max_period='32h', bouts=False)

Calculate periodogram for the range of periods.

If periods, powers = periodogram() then periods[powers.argmax()] returns the period with the maximal periodogram power.

Parameters
  • step (None|str|int|timedelta, optional) – Step size for the generating periods. The first period has a length of min_period, the second period has a length of min_period + step and so on. None stands for self.max_gap.

  • min_period (str|int|timedelta, optional) – Minimal period length, inclusive, defaults to '16h'.

  • max_period (str|int|timedelta, optional) – Maximal period length, inclusive, defaults to '32h'.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

Return type

(np.array[np.timedelta64], np.array[float])

Returns

(periods, powers) where

periods is periods in the range from min_period to max_period (inclusive).

powers is the corresponding periodogram powers for each period.

plot_periodogram(step=None, min_period='16h', max_period='32h', bouts=False, filename=None, width=1000, height=600, dpi=100)

Calculate and plot periodogram() for the range of periods.

The plot also includes an indication of the period with the maximal periodogram power.

Parameters
  • step (None|str|int|timedelta, optional) – Step size for the generating periods. The first period has a length of min_period, the second period has a length of min_period + step and so on. None stands for self.max_gap.

  • min_period (str|int|timedelta, optional) – Minimal period length, inclusive, defaults to '16h'.

  • max_period (str|int|timedelta, optional) – Maximal period length, inclusive, defaults to '32h'.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 600.

  • dpi (int, optional) – Plot resolution, defaults to 100.

light_activity(bouts=False, auc=False)

Calculate light phase activity.

Parameters
  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • auc (bool, optional) – Flag to return AUC, defaults to False.

Return type

(np.array[float], float)|(np.array[float], float, float)

Returns

(daily_values, total) or (daily_values, total, auc_val) if auc = True where

daily_values is the relative amplitude for each day;

total is the relative amplitude calculated from the data for all days;

auc_val is the area under the curve (integral).

plot_light_activity(bouts=False, filename=None, width=1000, height=600, dpi=100)

Calculate and plot light_activity() for each day.

The plot also includes total value based on data for all days.

Parameters
  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 600.

  • dpi (int, optional) – Plot resolution, defaults to 100.

interdaily_stability(step='1h', bouts=False)

Calculate interdaily stability.

Note

Small step values lead to high interdaily variability even with stable activity patterns.

Parameters
  • step (None|str|int|timedelta, optional) – Data discretization step used for calculations, defaults to None. None stands for self.max_gap.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

Return type

float

Returns

Number from 0 to 1 (inclusive).

intradaily_variability(step='1h', bouts=False, auc=False)

Calculate intradaily variability.

Note

Small step values lead to high interdaily variability even with stable activity patterns.

Parameters
  • step (None|str|int|timedelta, optional) – Data discretization step used for calculations, defaults to None. None stands for self.max_gap.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • auc (bool, optional) – Flag to return AUC, defaults to False.

Return type

(np.array[float], float)|(np.array[float], float, float)

Returns

(daily_values, total) or (daily_values, total, auc_val) if auc = True where

daily_values is the relative amplitude for each day;

total is the relative amplitude calculated from the data for all days;

auc_val is the area under the curve (integral).

plot_intradaily_variability(step='1h', bouts=False, filename=None, width=1000, height=600, dpi=100)

Calculate and plot intradaily_variability() for each day.

The plot also includes the total value based on data for all days.

Parameters
  • step (None|str|int|timedelta, optional) – Data discretization step used for calculations, defaults to None. None stands for self.max_gap.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 600.

  • dpi (int, optional) – Plot resolution, defaults to 100.

relative_amplitude(most_active='10h', least_active='5h', bouts=False, auc=False)

Calculate relative amplitude.

Parameters
  • most_active (str|int|timedelta, optional) – Length of the most active period.

  • least_active (str|int|timedelta, optional) – Length of the least active period.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

Return type

(np.array[float], float)|(np.array[float], float, float)

Returns

(daily_values, total) or (daily_values, total, auc_val) if auc = True where

daily_values is the relative amplitude for each day;

total is the relative amplitude calculated from the data for all days;

auc_val is the area under the curve (integral).

plot_relative_amplitude(most_active='10h', least_active='5h', bouts=False, filename=None, width=1000, height=600, dpi=100)

Calculate and plot relative_amplitude() for each day.

The plot also includes the total value based on data for all days.

Parameters
  • most_active (str|int|timedelta, optional) – Length of the most active period.

  • least_active (str|int|timedelta, optional) – Length of the least active period.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 600.

  • dpi (int, optional) – Plot resolution, defaults to 100.

daily_bouts(max_gap=None, min_duration=None, min_activity=None)

Calculate daily activity bout statistics.

At first it calculates activity bouts based on provided parameters and then for each day, it calculates the number of activity bouts and the average activity bout duration.

Parameters
  • max_gap (None|str|int|timedelta, optional) – Overrides self.max_gap if specified.

  • min_duration (None|str|int|timedelta, optional) – Overrides self.min_duration if specified.

  • min_activity (None|int, optional) – Overrides self.min_activity if specified.

Return type

(np.array[int], np.array[float])

Returns

(bout_counts, bout_durations) where

bout_counts is the number of activity bouts in each day;

bout_durations is the average activity bout durations for each day in minutes.

plot_daily_bouts(max_gap=None, min_duration=None, min_activity=None, filename=None, width=1000, height=600, dpi=100)

Calculate and plot daily_bouts() statistics.

Bout counts are plotted as a bar plot vs left y-axis and average bout durations are plotted as a line plot vs right y-axis.

Parameters
  • max_gap (None|str|int|timedelta, optional) – Overrides self.max_gap if specified.

  • min_duration (None|str|int|timedelta, optional) – Overrides self.min_duration if specified.

  • min_activity (None|int, optional) – Overrides self.min_activity if specified.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 600.

  • dpi (int, optional) – Plot resolution, defaults to 100.

plot_bout_histogram(max_gap=None, min_duration=None, min_activity=None, bins=50, filename=None, width=1000, height=600, dpi=100)

Plot histogram of activity_bouts() duration distribution.

Parameters
  • max_gap (None|str|int|timedelta, optional) – Overrides self.max_gap if specified.

  • min_duration (None|str|int|timedelta, optional) – Overrides self.min_duration if specified.

  • min_activity (None|int, optional) – Overrides self.min_activity if specified.

  • bins (int|np.array[int], optional) – Number of bins or array of bin edges.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 600.

  • dpi (int, optional) – Plot resolution, defaults to 100.

activity_onset(step=None, percentile=20, N='6h', M='6h', bouts=False, mode='step')

Calculate activity onset for each day.

Activity onset for a given day is calculated as follows:

  1. Split the day into intervals of a uniform length (discretize it with some step).

  2. Denote all points where activity is less than the percentile of non-zero activity as being inactive.

  3. Set activity values of all inactive points to -1 and all other points to 1.

  4. Apply the convolution kernel with the left part being negative and the right part being positive to the activity values. This will smoothly summarize the activity values.

  5. Find the point with the maximal summarized value. This will be the activity onset.

Parameters
  • step (None|str|int|timedelta, optional) – Data discretization step used for calculations, defaults to None. None stands for self.max_gap.

  • percentile (int, optional) – Percentile of the daily non-zero activity to use as a threshold for activity onset calculation. Activity lower than that is treated as a zero activity.

  • N (str|int|timedelta, optional) – Length of the period of negative values (daytime, left part) in the convolution kernel for activity onset calculation.

  • M (str|int|timedelta, optional) – Length of the period of positive values (nighttime, right part) in the convolution kernel for activity onset calculation.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • mode (str, optional) –

    Defines the shape of the convolution kernel. Each kernel has a discontinuity where left and righ parts connect (the values around the discontinuity are -1 and 1 for the left and right parts respectively). For kernels other than step the left part is decreasing from 0 to -1 and the right part is decreasing from 1 to 0, i.e. the parts of the kernel further from the discontinuity are given less weight.

    The possible kernels are:

    'step' – function with uniform left and right parts.

    'linear' – linear function of the relative distance to the discontinuity.

    'quadratic' – quadratic function (square root) of the relative distance to the discontinuity.

    'sine' – function proportional to a shifted sine (cosine) of the relative distance to the discontinuity.

Return type

np.array[np.datetime64]

Returns

Activity onsets for each day.

Note

The returned array contains timestamps (absolute time values of an activity onset), not relative shifts from the start of the day.

plot_activity_onset(step=None, percentile=20, N='6h', M='6h', bouts=False, mode='step', filename=None, width=1000, height=600, dpi=100)

Calculate and plot activity_onset() for each day.

Activity onsets for each day are plotted as shifts (in hours) from the start of the day (the maximal possible value is 24).

Parameters
  • step (None|str|int|timedelta, optional) – Data discretization step used for calculations, defaults to None. None stands for self.max_gap.

  • percentile (int, optional) – Percentile of the daily non-zero activity to use as a threshold for activity onset calculation. Activity lower than that is treated as a zero activity.

  • N (str|int|timedelta, optional) – Length of the period of negative values (daytime, left part) in the convolution kernel for activity onset calculation.

  • M (str|int|timedelta, optional) – Length of the period of positive values (nighttime, right part) in the convolution kernel for activity onset calculation.

  • bouts (bool, optional) – Indicates that the calculation is based on the number of activity bouts rather then activity events, defualts to False.

  • mode (str, optional) –

    Defines the shape of the convolution kernel. Each kernel has a discontinuity where left and righ parts connect (the values around the discontinuity are -1 and 1 for the left and right parts respectively). For kernels other than step the left part is decreasing from 0 to -1 and the right part is decreasing from 1 to 0, i.e. the parts of the kernel further from the discontinuity are given less weight.

    The possible kernels are:

    'step' – function with uniform left and right parts.

    'linear' – linear function of the relative distance to the discontinuity.

    'quadratic' – quadratic function (square root) of the relative distance to the discontinuity.

    'sine' – function proportional to a shifted sine (cosine) of the relative distance to the discontinuity.

  • filename (None|str|PathLike, optional) – Name of the file where the graph is saved. If not specified then the graph is displayed on the screen.

  • width (int, optional) – Plot width in pixels, defaults to 1000.

  • height (int, optional) – Plot height in pixels, defaults to 600.

  • dpi (int, optional) – Plot resolution, defaults to 100.

chronobiology.chronobiology.generate_data(points_per_day=100, days=10, activity_period='24h', night_period='24h', bg_ratio=0.2, multiactivity=False)

Generate random input data.

Parameters
  • points_per_day (int) – Number of measurement points per day, defaults to 100.

  • days (int) – Series length in days, defaults to 10.

  • activity_period (str|int|timedelta) – Activity period length, defaults to '24h'.

  • night_period (str|int|timedelta) – Period of night, defaults to '24h'.

  • bg_ratio (float) – Backgraoud activity ratio, defaults to 0.2.

  • multiactivity (bool) – Multiactivity, defaults to False.

Return type

dict[str: np.array[np.datetime64]|np.array[float]|np.array[bool]]

Returns

Dictionary containing arrays 'time', 'value' and 'is_night' ready to be used as input for the CycleAnalyzer constructor.

Usage example

>>> data = generate_data()
>>> ca = CycleAnalyzer(data['time'], data['value'], data['is_night'])
chronobiology.chronobiology.generate_night(timeseries, night_period='24h')

Generate a random is_night array.

Parameters
  • timeseries (np.array[np.datetime64]) – Timestamps of measurements.

  • night_period (str|int|timedelta, optional) – Period of night, defualts to '24h'.

Return type

np.array[bool]

Returns

Array denoting whether night (True) or day (False) is associated with a corresponding measurement.

Indices and tables