Source code for pyActigraphy.light.gendevice

#############################################################################
# Copyright (c) 2022, Daylight Academy
# Author: Grégory Hammad
# Owner: Daylight Academy (https://daylight.academy)
# Maintainer: Grégory Hammad
# Email: gregory.hammad@uliege.be
# Status: development
#############################################################################
# The development of a 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/.
#
# This module is also part of the pyActigraphy software.
# pyActigraphy is a free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# pyActigraphy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
############################################################################
import pandas as pd
import os

from .light import LightRecording


[docs]class GenLightDevice(LightRecording): r"""Generic light acquisition device Parameters ---------- input_fname: str Path to the file. channels: list of str, optional Select channels to read from the input file. If the list is empty, all channels are read. Default is \[\]. rsfreq: str, optional Resampling frequency. Cf. #timeseries-offset-aliases in <https://pandas.pydata.org/pandas-docs/stable/timeseries.html>. Default is None. agg: str, optional Aggregation function to use when resampling. Default is 'mean'. log10_transform: bool, optional If set to True, data are (log10\+1)-transformed. Default is True. start_time: datetime-like, optional Read data from this time. Default is None. period: str, optional Length of the read data. Cf. #timeseries-offset-aliases in <https://pandas.pydata.org/pandas-docs/stable/timeseries.html>. Default is None (i.e all the data). dayfirst: bool, optional If set to True, the timestamps are parsed as DD/MM/YYYY """ def __init__( self, input_fname, channels=[], rsfreq=None, agg='mean', log10_transform=True, start_time=None, period=None, dayfirst=True ): # get absolute file path input_fname = os.path.abspath(input_fname) # [TO-DO] check if file exists # [TO-DO] check it is has the right file extension .awd # Extracting data data = pd.read_csv( input_fname, index_col='UTC Timestamp', parse_dates=True, infer_datetime_format=True, dayfirst=dayfirst ) # Extracting UUID uuid = data.loc[:, 'Device ID'].unique() if uuid.size != 1: raise ValueError( 'The UUID retrieved from the input file is {}.'.format( 'missing' if uuid.size == 0 else 'not unique: {}'.format( ', '.join(uuid) ) ) ) else: uuid = uuid[0] # Drop UUID column once it has been extracted data.drop(columns=['Device ID'], inplace=True) # Resampling, if required and possible. if rsfreq is None: if data.index.inferred_freq is not None: data = data.asfreq(data.index.inferred_freq) else: raise ValueError( "The acquisition frequency could not be retrieved from the" " data and no resampling frequency was not provided by the" " user.\nPlease specify the input parameter 'rsfrq' in" " order to overcome this issue." ) else: data = data.resample(rsfreq).agg(agg) # Restricting data to start/stop times, if required. if start_time is not None: start_time = pd.to_datetime(start_time) else: start_time = data.index[0] if period is not None: period = pd.Timedelta(period) stop_time = start_time+period else: stop_time = data.index[-1] period = stop_time - start_time data = data.loc[start_time:stop_time] # Extracting other metadata self.__cct = self.__extract_from_data( data, 'CCT in K' ) self.__duv = self.__extract_from_data( data, 'Duv' ) self.__tilt = self.__extract_from_data( data, 'Tilt in °' ) self.__usertriggered = self.__extract_from_data( data, 'TriggeredByUser' ) # Drop metadata data.drop( columns=['CCT in K', 'Duv', 'Tilt in °', 'TriggeredByUser'], inplace=True, errors='raise' # Change to ignore optional columns ) # call __init__ function of the base class super().__init__( name=os.path.basename(input_fname), uuid=uuid, data=data[ [col for col in data.columns if ((col in channels) if channels else True)] ], frequency=data.index.freq.delta, log10_transform=log10_transform ) self.start_time = start_time self.period = period @classmethod def __extract_from_data(cls, data, key): if key in data.columns: return data[key] else: return None @property def cct(self): r"""Value of the CCT (in K).""" return self.__cct @property def duv(self): r"""Value of the delta u,v.""" return self.__duv @property def tilt(self): r"""Value of the tilt (in °).""" return self.__tilt @property def triggered_by_user(self): r"""Value of the marker 'TriggeredByUser'.""" return self.__usertriggered.round(0).astype(bool)
def read_raw_gld( input_fname, channels=[], rsfreq=None, agg='mean', log10_transform=True, start_time=None, period=None, dayfirst=True ): r"""Reader function for generic light device file. Parameters ---------- input_fname: str Path to the file. channels: list of str, optional Select channels to read from the input file. If the list is empty, all channels are read. Default is \[\]. rsfreq: str, optional Resampling frequency. Cf. #timeseries-offset-aliases in <https://pandas.pydata.org/pandas-docs/stable/timeseries.html>. Default is None. agg: str, optional Aggregation function to use when resampling. Default is 'mean'. log10_transform: bool, optional If set to True, data are (log10\+1)-transformed. Default is True. start_time: datetime-like, optional Read data from this time. Default is None. period: str, optional Length of the read data. Cf. #timeseries-offset-aliases in <https://pandas.pydata.org/pandas-docs/stable/timeseries.html>. Default is None (i.e all the data). dayfirst: bool, optional If set to True, the timestamps are parsed as DD/MM/YYYY Returns ------- raw : Instance of GenLightDevice An object containing raw GLD data """ return GenLightDevice( input_fname, channels=channels, rsfreq=rsfreq, agg=agg, log10_transform=log10_transform, start_time=start_time, period=period, dayfirst=dayfirst )