diff --git a/docs/source/index.rst b/docs/source/index.rst index fa91657..22406e9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -41,10 +41,10 @@ examples and more information on the functions can be found in the documentation data, sample_freq = paat.read_gt3x('path/to/gt3x/file') # Detect non-wear time - data.loc[:, "Non Wear Time"] = paat.detect_non_wear_time_syed2021(data, sample_freq) + data.loc[:, "Non Wear Time"] = paat.detect_non_wear_time_hees2011(data, sample_freq) # Detect sleep episodes - data.loc[:, "Sleep"] = paat.detect_sleep_weitz2022(data, sample_freq) + data.loc[:, "Sleep"] = paat.detect_time_in_bed_weitz2024(data, sample_freq) # Classify moderate-to-vigorous and sedentary behavior data.loc[:, ["MVPA", "SB"]] = paat.calculate_pa_levels(data, sample_freq) @@ -58,9 +58,6 @@ examples and more information on the functions can be found in the documentation .. note:: - In this example, methods of `Syed et al. (2021) `_ - and `Weitz et al. (2022) `_ and activity - thresholds of `Sanders et al. (2019) `_. - However, these are only examples. There are multiple methods implemented in PAAT + Note that these are only examples. There are multiple methods implemented in PAAT and the processing pipeline can easily be adjusted to individual needs. More examples can be found in the examples section. diff --git a/paat/__init__.py b/paat/__init__.py index 88622c1..d893567 100644 --- a/paat/__init__.py +++ b/paat/__init__.py @@ -31,7 +31,7 @@ from .estimates import calculate_pa_levels, create_activity_column from .features import calculate_actigraph_counts, calculate_vector_magnitude, calculate_brond_counts from .io import read_gt3x -from .sleep import detect_sleep_weitz2022, detect_sleep_triaxial_weitz2022 +from .sleep import detect_sleep_weitz2022, detect_sleep_triaxial_weitz2022, detect_time_in_bed_weitz2024 from .wear_time import detect_non_wear_time_naive, detect_non_wear_time_hees2011, detect_non_wear_time_syed2021 try: diff --git a/paat/models/TIB_model.h5 b/paat/models/TIB_model.h5 new file mode 100644 index 0000000..76885b2 Binary files /dev/null and b/paat/models/TIB_model.h5 differ diff --git a/paat/sleep.py b/paat/sleep.py index 75b1d21..90ae254 100644 --- a/paat/sleep.py +++ b/paat/sleep.py @@ -12,9 +12,16 @@ import numpy as np from torch import nn import torch +import tensorflow as tf +from tensorflow.keras import models -from . import features +# Hide GPU from visible devices +tf.config.set_visible_devices([], 'GPU') +#tf.compat.v1.disable_eager_execution() + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' +from . import features class _SleepModel(nn.Module): def __init__(self, input_dim, hid_dim, output_dim, n_layers, dropout, batch_first=False): @@ -49,6 +56,9 @@ def detect_sleep_weitz2022(data, sample_freq, means=None, stds=None): """ Infer time in bed from raw acceleration signal using frequency features. + .. warning:: + This method turned out to work not as accurately as initially thought. Use with care! + Parameters ---------- data : DataFrame @@ -101,6 +111,9 @@ def detect_sleep_triaxial_weitz2022(data, sample_freq, resampled_frequency="1min """ Infer time in bed from raw acceleration signal. + .. warning:: + This method turned out to work not as accurately as initially thought. Use with care! + Parameters ---------- data : DataFrame @@ -154,3 +167,56 @@ def detect_sleep_triaxial_weitz2022(data, sample_freq, resampled_frequency="1min predictions = np.repeat(predictions, seconds * sample_freq) return predictions + +def detect_time_in_bed_weitz2024(data, sample_freq, resampled_frequency="1min", means=None, stds=None, model=None): + """ + Infer time in bed from raw acceleration signal. + + Parameters + ---------- + data : DataFrame + a DataFrame containg the raw acceleration data + sample_freq : int + the sampling frequency in which the data was recorded + resampled_frequency : str (optional) + a str indicating to what frequency the data should be resampled. This depends + on the model used to predict, defaults to 1min. + means : array_like (optional) + a numpy array with the channel means, will be calculated for the sample + if not specified + stds : array_like (optional) + a numpy array with the channel stds, will be calculated for the sample + if not specified + model : nn.Module (optional) + a loaded pytorch custom model. + + Returns + ------- + predicted_time_in_bed : np.array (n_samples,) + a numpy array indicating whether the values of the acceleration data were spent in bed + + """ + if resampled_frequency: + data = data[['X', 'Y', 'Z']].resample(resampled_frequency).mean() + + X = data.reset_index()[["Y", "X", "Z"]].values.copy() + + # If no means and stds are given, calculate subject's mean and std + # to normalize by this + if not means or not stds: + means, stds = X.mean(axis=0), X.std(axis=0) + + # Normalize input + X = (X - means) / stds + + # Load model if not specified + if not model: + model_path = os.path.join(os.path.pardir, os.path.dirname(__file__), 'models', 'TIB_model.h5') + model = models.load_model(model_path) + + predictions = (model.predict(X[np.newaxis], verbose=0).squeeze() >= .5) + + seconds = pd.Timedelta(resampled_frequency).seconds + predictions = np.repeat(predictions, seconds * sample_freq) + + return predictions diff --git a/pyproject.toml b/pyproject.toml index 709fea5..227e0e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "paat" -version = "1.0.0-beta.2" +version = "1.0.0b3" description = "A comprehensive toolbox to analyse and model raw physical activity data" license = "MIT" @@ -28,15 +28,14 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.9,<3.11" # Compatible python versions must be declared here -pandas = "^1.2.4" -scipy = "^1.6.2" +pandas = "^1.5.3" psutil = "^5.8.0" glob2 = "^0.7" resampy = "^0.2.2" joblib = "^1.0.1" -numpy = "^1.20.3" +numpy = "1.23.5" bitstring = "^3.1.7" -tensorflow = "^2.7.0" +tensorflow = "^2.11.0" torch = "^1.10.1" agcounts = "^0.1.1" toml = "^0.10.2"