circStudio 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- circstudio/.DS_Store +0 -0
- circstudio/__init__.py +7 -0
- circstudio/analysis/__init__.py +11 -0
- circstudio/analysis/cosinor/__init__.py +2 -0
- circstudio/analysis/cosinor/cosinor.py +278 -0
- circstudio/analysis/flm/__init__.py +3 -0
- circstudio/analysis/flm/flm.py +402 -0
- circstudio/analysis/fractal/__init__.py +4 -0
- circstudio/analysis/fractal/fractal.py +727 -0
- circstudio/analysis/lids/__init__.py +3 -0
- circstudio/analysis/lids/lids.py +605 -0
- circstudio/analysis/lids/likelihood.py +112 -0
- circstudio/analysis/lids/model_functions.py +80 -0
- circstudio/analysis/lids/signal_functions.py +78 -0
- circstudio/analysis/lids/smoothing.py +38 -0
- circstudio/analysis/lids/transforms.py +63 -0
- circstudio/analysis/metrics/__init__.py +2 -0
- circstudio/analysis/metrics/feature_screening.py +442 -0
- circstudio/analysis/metrics/metrics.py +1854 -0
- circstudio/analysis/models/__init__.py +3 -0
- circstudio/analysis/models/light_tools.py +778 -0
- circstudio/analysis/models/math_models.py +3010 -0
- circstudio/analysis/models/tools.py +306 -0
- circstudio/analysis/sleep/__init__.py +3 -0
- circstudio/analysis/sleep/diary.py +406 -0
- circstudio/analysis/sleep/scoring/__init__.py +5 -0
- circstudio/analysis/sleep/scoring/csm.py +235 -0
- circstudio/analysis/sleep/scoring/roenneberg.py +354 -0
- circstudio/analysis/sleep/scoring/smp.py +225 -0
- circstudio/analysis/sleep/scoring/sri.py +164 -0
- circstudio/analysis/sleep/scoring/utils.py +441 -0
- circstudio/analysis/sleep/sleep.py +1910 -0
- circstudio/analysis/sleep/sleep_tools.py +206 -0
- circstudio/analysis/ssa/__init__.py +3 -0
- circstudio/analysis/ssa/ssa.py +557 -0
- circstudio/analysis/tools.py +476 -0
- circstudio/data/.DS_Store +0 -0
- circstudio/data/example_01.AWD +18408 -0
- circstudio/data/example_01_mask.AWD +18408 -0
- circstudio/data/example_01_sleepdiary.ods +0 -0
- circstudio/data/example_01_sleepdiary_extra_states.ods +0 -0
- circstudio/data/example_02.AWD +18420 -0
- circstudio/data/example_03.AWD +21463 -0
- circstudio/data/example_04.AWD +31306 -0
- circstudio/data/example_05.AWD +21710 -0
- circstudio/data/example_masklog.csv +3 -0
- circstudio/data/example_sstlog.csv +4 -0
- circstudio/data/example_sstlog.ods +0 -0
- circstudio/data/example_sstlog.xls +0 -0
- circstudio/data/example_sstlog.xlsx +0 -0
- circstudio/data/sample-summary-no-calibration.json +32 -0
- circstudio/data/sample-summary-wrong-name.json +32 -0
- circstudio/data/sample-summary.json +837 -0
- circstudio/data/sample-timeSeries.csv.gz +0 -0
- circstudio/data/sleepdiary_atr.ods +0 -0
- circstudio/data/test_sample.AWD +11725 -0
- circstudio/data/test_sample.agd +0 -0
- circstudio/data/test_sample.csv +11712 -0
- circstudio/data/test_sample.mtn +1234 -0
- circstudio/data/test_sample_atr.txt +5785 -0
- circstudio/data/test_sample_aw4.AWD +31861 -0
- circstudio/data/test_sample_aw7.AWD +30630 -0
- circstudio/data/test_sample_awi.AWD +6169 -0
- circstudio/data/test_sample_awl.AWD +12781 -0
- circstudio/data/test_sample_awlp.AWD +10110 -0
- circstudio/data/test_sample_awmk2.AWD +29999 -0
- circstudio/data/test_sample_aws.AWD +1528 -0
- circstudio/data/test_sample_awt.AWD +3869 -0
- circstudio/data/test_sample_dqt.csv +43215 -0
- circstudio/data/test_sample_mesa.csv +5 -0
- circstudio/data/test_sample_rpx_eng.csv +20308 -0
- circstudio/data/test_sample_rpx_fr.csv +11712 -0
- circstudio/data/test_sample_rpx_ger_no_light.csv +20313 -0
- circstudio/data/test_sample_rpx_ger_with_light.csv +20316 -0
- circstudio/data/test_sample_tal.txt +10088 -0
- circstudio/io/.DS_Store +0 -0
- circstudio/io/__init__.py +10 -0
- circstudio/io/agd/__init__.py +5 -0
- circstudio/io/agd/agd.py +252 -0
- circstudio/io/atr/__init__.py +5 -0
- circstudio/io/atr/atr.py +133 -0
- circstudio/io/awd/__init__.py +4 -0
- circstudio/io/awd/awd.py +279 -0
- circstudio/io/base.py +194 -0
- circstudio/io/dqt/__init__.py +5 -0
- circstudio/io/dqt/dqt.py +171 -0
- circstudio/io/mask.py +395 -0
- circstudio/io/mesa/__init__.py +4 -0
- circstudio/io/mesa/mesa.py +215 -0
- circstudio/io/rpx/__init__.py +4 -0
- circstudio/io/rpx/multilang.py +112 -0
- circstudio/io/rpx/rpx.py +407 -0
- circstudio/io/tal/__init__.py +5 -0
- circstudio/io/tal/tal.py +217 -0
- circstudio-1.0.0.dist-info/METADATA +807 -0
- circstudio-1.0.0.dist-info/RECORD +97 -0
- circstudio-1.0.0.dist-info/WHEEL +4 -0
circstudio/.DS_Store
ADDED
|
Binary file
|
circstudio/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .metrics.metrics import *
|
|
2
|
+
from .cosinor.cosinor import *
|
|
3
|
+
from .models.light_tools import *
|
|
4
|
+
from .models.math_models import *
|
|
5
|
+
from .models.tools import *
|
|
6
|
+
from .sleep.sleep import *
|
|
7
|
+
from .metrics.feature_screening import *
|
|
8
|
+
from .ssa import SSA
|
|
9
|
+
from .fractal import Fractal
|
|
10
|
+
from .flm import FLM
|
|
11
|
+
from .lids import LIDS
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from lmfit import fit_report, minimize, Parameters
|
|
4
|
+
import plotly.graph_objects as go
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Cosinor:
|
|
8
|
+
"""
|
|
9
|
+
Class for Cosinor analysis. Cosinor analysis fits a cosine curve to a time series
|
|
10
|
+
to summarize a rhythmic pattern using a small set of interpretable parameters.
|
|
11
|
+
|
|
12
|
+
It answers questions like:
|
|
13
|
+
- What is the typical baseline level of the signal? -> **Mesor**
|
|
14
|
+
- How large is the rhythm around that baseline? -> **Amplitude**
|
|
15
|
+
- When does the peak occur within the cycle? -> **Acrophase**
|
|
16
|
+
- What cycle length best matches the data? -> **Period**
|
|
17
|
+
|
|
18
|
+
Model
|
|
19
|
+
-----
|
|
20
|
+
This implementation uses a standard 1-harmonic cosinor model:
|
|
21
|
+
|
|
22
|
+
y(x) = Mesor + Amplitude * cos(2π/Period * x + Acrophase)
|
|
23
|
+
|
|
24
|
+
where `x`is time expressed in sample units (e.g., minutes since start),
|
|
25
|
+
not necessarily clock time.
|
|
26
|
+
|
|
27
|
+
Typical usage
|
|
28
|
+
-------------
|
|
29
|
+
>>> model = Cosinor()
|
|
30
|
+
>>> result = model.fit(series)
|
|
31
|
+
>>> mesor = result.params['Mesor'].value
|
|
32
|
+
>>> amplitude = result.params['Amplitude'].value
|
|
33
|
+
>>> period = result.params['Period'].value
|
|
34
|
+
>>> acrophase = result.params['Acrophase'].value
|
|
35
|
+
>>> best = model.best_fit(series, result.params)
|
|
36
|
+
>>> fig = model.plot(series, result.params)
|
|
37
|
+
|
|
38
|
+
References
|
|
39
|
+
----------
|
|
40
|
+
This code is derived from the original implementation in pyActigraphy, distributed under the BSD 3-Clause License.
|
|
41
|
+
Original author: Grégory Hammad (gregory.hammad@uliege.be).
|
|
42
|
+
|
|
43
|
+
[1] Hammad, G., Reyt, M., Beliy, N., Baillet, M., Deantoni, M., Lesoinne, A., Muto, V., & Schmidt, C. (2021).
|
|
44
|
+
pyActigraphy: Open-source python package for actigraphy data visualization and analysis.
|
|
45
|
+
PLoS Computational Biology, 17(10), 1009514–1009535. https://doi.org/10.1371/journal.pcbi.1009514
|
|
46
|
+
|
|
47
|
+
[2] Hammad, G., Wulff, K., Skene, D. J., Münch, M., & Spitschan, M. (2024). Open-Source Python Module for the
|
|
48
|
+
Analysis of Personalized Light Exposure Data from Wearable Light Loggers and Dosimeters.
|
|
49
|
+
LEUKOS, 20(4), 380–389. https://doi.org/10.1080/15502724.2023.2296863
|
|
50
|
+
|
|
51
|
+
[3] Cornelissen, G. (2014). Cosinor-based rhythmometry. Theoretical Biology and Medical Modelling, 11(1), 16.
|
|
52
|
+
https://doi.org/10.1186/1742-4682-11-16
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
@staticmethod
|
|
56
|
+
def _cosinor(x, params):
|
|
57
|
+
"""
|
|
58
|
+
1-harmonic cosine function
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
x : np.ndarray
|
|
63
|
+
Time values expressed in sample units (e.g., minutes since start).
|
|
64
|
+
params : lmfit.Parameters
|
|
65
|
+
Must contain: 'Amplitude', 'Acrophase', 'Period', 'Mesor'.
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
np.ndarray
|
|
70
|
+
Model values evaluated at `x`.
|
|
71
|
+
"""
|
|
72
|
+
A = params['Amplitude']
|
|
73
|
+
phi = params['Acrophase']
|
|
74
|
+
T = params['Period']
|
|
75
|
+
M = params['Mesor']
|
|
76
|
+
return M + A * np.cos(2 * np.pi / T * x + phi)
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def _residual(params, x, data, fit_func):
|
|
80
|
+
"""
|
|
81
|
+
Residuals to be minimized by the optimizer.
|
|
82
|
+
|
|
83
|
+
Residuals are simply: observed - model.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
params : lmfit.Parameters
|
|
88
|
+
Candidate parameter values during optimization.
|
|
89
|
+
x : np.ndarray
|
|
90
|
+
Time values (in sample units).
|
|
91
|
+
data : np.ndarray
|
|
92
|
+
Observed data values.
|
|
93
|
+
fit_func : callable
|
|
94
|
+
Model function such as `_cosinor`.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
np.ndarray
|
|
99
|
+
Residual vector used by the optimizer.
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
model = fit_func(x, params)
|
|
103
|
+
return data - model
|
|
104
|
+
|
|
105
|
+
def __init__(self,fit_params=None):
|
|
106
|
+
"""
|
|
107
|
+
Create a Cosinor model with default or user-provided initial parameters.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
fit_params : lmfit.Parameters, optional
|
|
112
|
+
Initial guesses and bounds for the fit. If None, sensible defaults
|
|
113
|
+
are created.
|
|
114
|
+
"""
|
|
115
|
+
self.fit_func = self.__class__._cosinor
|
|
116
|
+
self.fit_obj_func = self.__class__._residual
|
|
117
|
+
|
|
118
|
+
if fit_params is None:
|
|
119
|
+
fit_params = Parameters()
|
|
120
|
+
# Default parameters for the cosinor fit function
|
|
121
|
+
fit_params.add('Amplitude', value=50, min=0)
|
|
122
|
+
fit_params.add('Acrophase', value=np.pi, min=0, max=2*np.pi)
|
|
123
|
+
fit_params.add('Period', value=1440, min=0)
|
|
124
|
+
fit_params.add('Mesor', value=50, min=0)
|
|
125
|
+
self.fit_initial_params = fit_params
|
|
126
|
+
|
|
127
|
+
# ----------------------------
|
|
128
|
+
# Time handling
|
|
129
|
+
# ----------------------------
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def _convert_timestamp_to_index(data):
|
|
133
|
+
"""Convert timestamps"""
|
|
134
|
+
# Define the x range by converting timestamps to indices, in order to
|
|
135
|
+
# deal with time series with irregular index.
|
|
136
|
+
return ((data.index - data.index[0]) / data.index.freq).values
|
|
137
|
+
|
|
138
|
+
def fit(
|
|
139
|
+
self,
|
|
140
|
+
data,
|
|
141
|
+
params=None,
|
|
142
|
+
method='leastsq',
|
|
143
|
+
nan_policy='raise',
|
|
144
|
+
reduce_fcn=None,
|
|
145
|
+
verbose=False
|
|
146
|
+
):
|
|
147
|
+
"""
|
|
148
|
+
Fit the actigraphy data using a cosinor function.
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
data : pandas.Series
|
|
153
|
+
Input time series.
|
|
154
|
+
params: instance of Parameters [1]_, optional.
|
|
155
|
+
Initial fit parameters. If None, use the default parameters.
|
|
156
|
+
Default is None.
|
|
157
|
+
method: str, optional
|
|
158
|
+
Name of the fitting method to use [1]_.
|
|
159
|
+
Default is 'leastsq'.
|
|
160
|
+
nan_policy: str, optional
|
|
161
|
+
Specifies action if the objective function returns NaN values.
|
|
162
|
+
One of:
|
|
163
|
+
|
|
164
|
+
* 'raise': a ValueError is raised
|
|
165
|
+
* 'propagate': the values returned from userfcn are un-altered
|
|
166
|
+
* 'omit': non-finite values are filtered
|
|
167
|
+
|
|
168
|
+
Default is 'raise'.
|
|
169
|
+
reduce_fcn: str, optional
|
|
170
|
+
Function to convert a residual array to a scalar value for the
|
|
171
|
+
scalar minimizers. Optional values are:
|
|
172
|
+
|
|
173
|
+
* 'None' : sum of squares of residual
|
|
174
|
+
* 'negentropy' : neg entropy, using normal distribution
|
|
175
|
+
* 'neglogcauchy': neg log likelihood, using Cauchy distribution
|
|
176
|
+
|
|
177
|
+
Default is None.
|
|
178
|
+
verbose: bool, optional
|
|
179
|
+
If set to True, display fit informations.
|
|
180
|
+
Default is False.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
fit_results : MinimizerResult
|
|
185
|
+
Fit results.
|
|
186
|
+
|
|
187
|
+
References
|
|
188
|
+
----------
|
|
189
|
+
This code is derived from the original implementation in pyActigraphy, distributed under the BSD 3-Clause License.
|
|
190
|
+
Original author: Grégory Hammad (gregory.hammad@uliege.be).
|
|
191
|
+
|
|
192
|
+
[1] Hammad, G., Reyt, M., Beliy, N., Baillet, M., Deantoni, M., Lesoinne, A., Muto, V., & Schmidt, C. (2021).
|
|
193
|
+
pyActigraphy: Open-source python package for actigraphy data visualization and analysis.
|
|
194
|
+
PLoS Computational Biology, 17(10), 1009514–1009535. https://doi.org/10.1371/journal.pcbi.1009514
|
|
195
|
+
|
|
196
|
+
[2] Hammad, G., Wulff, K., Skene, D. J., Münch, M., & Spitschan, M. (2024). Open-Source Python Module for the
|
|
197
|
+
Analysis of Personalized Light Exposure Data from Wearable Light Loggers and Dosimeters.
|
|
198
|
+
LEUKOS, 20(4), 380–389. https://doi.org/10.1080/15502724.2023.2296863
|
|
199
|
+
|
|
200
|
+
[3] Non-Linear Least-Squares Minimization and Curve-Fitting for Python.
|
|
201
|
+
https://lmfit.github.io/lmfit-py/index.html
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
# Define the x range by converting timestamps to indices, in order to
|
|
205
|
+
# deal with time series with irregular index.
|
|
206
|
+
x = self._convert_timestamp_to_index(data)
|
|
207
|
+
|
|
208
|
+
# Minimize residuals
|
|
209
|
+
fit_results = minimize(
|
|
210
|
+
self.fit_obj_func,
|
|
211
|
+
self.fit_initial_params if params is None else params,
|
|
212
|
+
method=method,
|
|
213
|
+
args=(x, data.values, self.fit_func),
|
|
214
|
+
nan_policy=nan_policy,
|
|
215
|
+
reduce_fcn=reduce_fcn
|
|
216
|
+
)
|
|
217
|
+
# Print fit parameters if verbose
|
|
218
|
+
if verbose:
|
|
219
|
+
print(fit_report(fit_results))
|
|
220
|
+
|
|
221
|
+
return fit_results
|
|
222
|
+
|
|
223
|
+
def best_fit(self, data, params):
|
|
224
|
+
"""
|
|
225
|
+
Best fit function of the data.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
data : pandas.Series
|
|
230
|
+
Originally fitted time series.
|
|
231
|
+
params: instance of Parameters [1]_
|
|
232
|
+
Best fit parameters.
|
|
233
|
+
|
|
234
|
+
Returns
|
|
235
|
+
-------
|
|
236
|
+
bestfit_data : pandas.Series
|
|
237
|
+
Time series of the best fit data.
|
|
238
|
+
|
|
239
|
+
References
|
|
240
|
+
----------
|
|
241
|
+
This code is derived from the original implementation in pyActigraphy, distributed under the BSD 3-Clause License.
|
|
242
|
+
Original author: Grégory Hammad (gregory.hammad@uliege.be).
|
|
243
|
+
|
|
244
|
+
[1] Hammad, G., Reyt, M., Beliy, N., Baillet, M., Deantoni, M., Lesoinne, A., Muto, V., & Schmidt, C. (2021).
|
|
245
|
+
pyActigraphy: Open-source python package for actigraphy data visualization and analysis.
|
|
246
|
+
PLoS Computational Biology, 17(10), 1009514–1009535. https://doi.org/10.1371/journal.pcbi.1009514
|
|
247
|
+
|
|
248
|
+
[2] Hammad, G., Wulff, K., Skene, D. J., Münch, M., & Spitschan, M. (2024). Open-Source Python Module for the
|
|
249
|
+
Analysis of Personalized Light Exposure Data from Wearable Light Loggers and Dosimeters.
|
|
250
|
+
LEUKOS, 20(4), 380–389. https://doi.org/10.1080/15502724.2023.2296863
|
|
251
|
+
|
|
252
|
+
[3] Non-Linear Least-Squares Minimization and Curve-Fitting for Python.
|
|
253
|
+
https://lmfit.github.io/lmfit-py/index.html
|
|
254
|
+
"""
|
|
255
|
+
# Define the x range by converting timestamps to indices, in order to
|
|
256
|
+
# deal with time series with irregular index.
|
|
257
|
+
x = self._convert_timestamp_to_index(data)
|
|
258
|
+
y = self.fit_func(x, params)
|
|
259
|
+
return pd.Series(index=data.index, data=y)
|
|
260
|
+
|
|
261
|
+
def plot(self, data, best_params):
|
|
262
|
+
layout = go.Layout(
|
|
263
|
+
title="Cosinor",
|
|
264
|
+
xaxis=dict(title="Date time"),
|
|
265
|
+
yaxis=dict(title="Counts/period"),
|
|
266
|
+
showlegend=False
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
fig = go.Figure(
|
|
270
|
+
data=[
|
|
271
|
+
go.Scatter(x=data.index.astype(str),
|
|
272
|
+
y=data, name='Raw data'),
|
|
273
|
+
go.Scatter(x=self.best_fit(data, best_params).index.astype(str),
|
|
274
|
+
y=self.best_fit(data, best_params),
|
|
275
|
+
name='Best fit')
|
|
276
|
+
], layout=layout,
|
|
277
|
+
)
|
|
278
|
+
return fig
|