PFun CMA Model -- Modular Demos¶
In [1]:
import pfun_cma_model as pfun
Instantiate the model¶
In [2]:
import json
cma = pfun.CMASleepWakeModel()
cma.to_dict()
Out[2]:
{'_DEFAULT_PARAMS_MODEL': {'N': 1024,
'd': 0.0,
'taup': 1.0,
'taug': 1.0,
'B': 0.05,
'Cm': 0.0,
'toff': 0.0,
'tM': [7.0, 11.0, 17.5],
'seed': None,
'eps': 1e-18},
'bounds': {'lb': [-12.0, 0.5, 0.1, 0.0, 0.0, -3.0],
'ub': [14.0, 3.0, 3.0, 1.0, 2.0, 3.0],
'keep_feasible': [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]},
'rng': None,
'_DEFAULT_PARAMS': {'N': 1024,
'd': 0.0,
'taup': 1.0,
'taug': 1.0,
'B': 0.05,
'Cm': 0.0,
'toff': 0.0,
'tM': [7.0, 11.0, 17.5],
'seed': None,
'eps': 1e-18},
'param_keys': ['N',
'd',
'taup',
'taug',
'B',
'Cm',
'toff',
'tM',
'seed',
'eps']}
In [3]:
cma.params
Out[3]:
CMAModelParams(N=1024, d=0.0, taup=1.0, taug=1.0, B=0.05, Cm=0.0, toff=0.0, tM=[7.0, 11.0, 17.5], seed=None, eps=1e-18)
Run the model (generate 24 hour-long bins)¶
In [4]:
cma.run()
Out[4]:
| t | c | m | a | I_S | I_E | L | g_0 | g_1 | g_2 | G | is_meal | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 days 00:00:00 | 0.000000 | 0.083045 | 0.853553 | 0.250723 | 0.152953 | 0.038349 | 1.106922e-11 | 0.100000 | 0.100000 | 0.100000 | 0.300000 | False |
| 0 days 00:01:24.457478005 | 0.023460 | 0.081543 | 0.855718 | 0.250761 | 0.151199 | 0.037915 | 1.354846e-11 | 0.100000 | 0.100000 | 0.100000 | 0.300000 | False |
| 0 days 00:02:48.914956010 | 0.046921 | 0.080067 | 0.857870 | 0.250800 | 0.149451 | 0.037482 | 1.656335e-11 | 0.100000 | 0.100000 | 0.100000 | 0.300000 | False |
| 0 days 00:04:13.372434019 | 0.070381 | 0.078618 | 0.860007 | 0.250840 | 0.147711 | 0.037052 | 2.022528e-11 | 0.100000 | 0.100000 | 0.100000 | 0.300000 | False |
| 0 days 00:05:37.829912024 | 0.093842 | 0.077194 | 0.862132 | 0.250881 | 0.145977 | 0.036623 | 2.466780e-11 | 0.100000 | 0.100000 | 0.100000 | 0.300000 | False |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 0 days 23:54:22.170087975 | 23.906158 | 0.021855 | 0.844762 | 0.249999 | 0.175555 | 0.043888 | 2.466780e-11 | 0.100005 | 0.100032 | 0.101864 | 0.301901 | False |
| 0 days 23:55:46.627565980 | 23.929619 | 0.021434 | 0.846979 | 0.249999 | 0.173500 | 0.043375 | 2.022528e-11 | 0.100005 | 0.100032 | 0.101830 | 0.301867 | False |
| 0 days 23:57:11.085043989 | 23.953079 | 0.021021 | 0.849184 | 0.249999 | 0.171457 | 0.042864 | 1.656335e-11 | 0.100005 | 0.100031 | 0.101797 | 0.301834 | False |
| 0 days 23:58:35.542521994 | 23.976540 | 0.020616 | 0.851375 | 0.249999 | 0.169424 | 0.042356 | 1.354846e-11 | 0.100005 | 0.100031 | 0.101765 | 0.301801 | False |
| 1 days 00:00:00 | 24.000000 | 0.020218 | 0.853553 | 0.249999 | 0.167403 | 0.041851 | 1.106922e-11 | 0.100005 | 0.100031 | 0.101734 | 0.301769 | False |
1024 rows × 12 columns
In [5]:
cma.run().plot(x='t', y='G', kind='line')
Out[5]:
<Axes: xlabel='t'>
Load & Process an example dataset¶
In [6]:
from pfun_cma_model.engine.fit import fit_model
from pfun_cma_model.engine.data_utils import format_data
from pfun_cma_model.misc.pathdefs import PFunDataPaths
df_original = PFunDataPaths().read_sample_data()
df = df_original.copy()
print('(raw) sample data')
print(df.tail().to_markdown())
print()
print('formatted sample data')
df_formatted = format_data(df)
print(df_formatted.tail().to_markdown())
print()
ax = df_formatted.plot(
x='time',
y=['G'],
title='Sample Glucose Dataset',
figsize=(10, 5),
linestyle='',
marker='o',
)
(raw) sample data | | Unnamed: 0.1 | Unnamed: 0 | user_id | ts_utc | ts_local | is_sg | sg | is_fl | meal_tag | tag_bef_meal | tag_after_meal | tag_after_snack | tag_seems_high | tag_seems_low | |-----:|---------------:|-------------:|----------:|:--------------------|:--------------------|--------:|-----:|--------:|-----------:|:---------------|:-----------------|:------------------|:-----------------|:----------------| | 2874 | 2874 | 2874 | 10130489 | 2021-01-28 07:08:28 | 2021-01-27 23:08:28 | 1 | 140 | 0 | nan | True | False | False | True | False | | 2875 | 2875 | 2875 | 10130489 | 2021-01-28 07:13:28 | 2021-01-27 23:13:28 | 1 | 142 | 0 | nan | True | False | False | True | False | | 2876 | 2876 | 2876 | 10130489 | 2021-01-28 07:18:28 | 2021-01-27 23:18:28 | 1 | 142 | 0 | nan | True | False | False | True | False | | 2877 | 2877 | 2877 | 10130489 | 2021-01-28 07:23:28 | 2021-01-27 23:23:28 | 1 | 142 | 0 | nan | True | False | False | True | False | | 2878 | 2878 | 2878 | 10130489 | 2021-01-28 07:28:28 | 2021-01-27 23:28:28 | 1 | 121 | 0 | nan | True | False | False | False | False | formatted sample data | t | time | value | tod | t | G | |--------:|:------------------------------------|--------:|--------:|--------:|--------:| | 23.8043 | 2021-01-21 23:43:09.470185456+00:00 | 126.333 | 23.8044 | 23.8043 | 1.37625 | | 23.8066 | 2021-01-25 23:40:15.322580100+00:00 | 126 | 23.8066 | 23.8066 | 1.36254 | | 23.8866 | 2021-01-20 23:47:22.102639092+00:00 | 115.333 | 23.8867 | 23.8866 | 1.13052 | | 23.8891 | 2021-01-24 23:44:27.955033736+00:00 | 123.333 | 23.8892 | 23.8891 | 1.29308 | | 23.93 | 2021-01-23 23:48:40.587487372+00:00 | 117.5 | 23.9301 | 23.93 | 1.1675 |
Zoom on Last day (raw data)¶
In [7]:
ax.set_xlim('2021-01-27 16:00:00', df_formatted['time'].max())
ax.get_figure()
Out[7]:
Fit the model to sample data¶
Raw fit result (solution)¶
In [49]:
print('model fit result')
result = fit_model(data=df_formatted, tcol='time', tm_freq='45min')
print(result.soln.shape)
print(result)
print()
model fit result
(1024, 13)
soln= t c m a \
0 days 00:00:00 0.000000 0.000936 3.181676e-17 0.250121
0 days 00:01:24.457478005 0.023460 0.000936 3.166205e-17 0.250123
0 days 00:02:48.914956010 0.046921 0.000935 3.150475e-17 0.250126
0 days 00:04:13.372434019 0.070381 0.000935 3.134493e-17 0.250129
0 days 00:05:37.829912024 0.093842 0.000934 3.118266e-17 0.250132
... ... ... ... ...
0 days 23:54:22.170087975 23.906158 0.000304 3.292593e-22 0.474829
0 days 23:55:46.627565980 23.929619 0.000303 3.475400e-22 0.473340
0 days 23:57:11.085043989 23.953079 0.000303 3.667490e-22 0.471854
0 days 23:58:35.542521994 23.976540 0.000302 3.869292e-22 0.470372
1 days 00:00:00 24.000000 0.000302 4.081252e-22 0.468895
I_S I_E L g_0 g_1 \
0 days 00:00:00 0.999785 0.250067 0.999995 0.288256 0.288256
0 days 00:01:24.457478005 0.999785 0.250070 0.999995 0.288256 0.288256
0 days 00:02:48.914956010 0.999785 0.250072 0.999995 0.288256 0.288256
0 days 00:04:13.372434019 0.999785 0.250075 0.999995 0.288256 0.288256
0 days 00:05:37.829912024 0.999785 0.250078 0.999995 0.288256 0.288256
... ... ... ... ... ...
0 days 23:54:22.170087975 0.999930 0.474796 1.000000 0.288282 0.288444
0 days 23:55:46.627565980 0.999930 0.473307 1.000000 0.288282 0.288440
0 days 23:57:11.085043989 0.999930 0.471821 1.000000 0.288281 0.288436
0 days 23:58:35.542521994 0.999931 0.470340 1.000000 0.288281 0.288432
1 days 00:00:00 0.999931 0.468862 1.000000 0.288280 0.288428
g_2 g_3 G is_meal
0 days 00:00:00 0.288256 0.288256 1.153024 False
0 days 00:01:24.457478005 0.288256 0.288256 1.153024 False
0 days 00:02:48.914956010 0.288256 0.288256 1.153024 False
0 days 00:04:13.372434019 0.288256 0.288256 1.153024 False
0 days 00:05:37.829912024 0.288256 0.288256 1.153024 False
... ... ... ... ...
0 days 23:54:22.170087975 0.290648 0.295124 1.162498 False
0 days 23:55:46.627565980 0.290578 0.294893 1.162192 False
0 days 23:57:11.085043989 0.290509 0.294672 1.161898 False
0 days 23:58:35.542521994 0.290444 0.294458 1.161614 False
1 days 00:00:00 0.290380 0.294253 1.161341 False
[1024 rows x 13 columns] formatted_data= time value tod \
t
0.021667 2021-01-21 00:01:18.484847970+00:00 113.666667 0.021667
0.043056 2021-01-24 00:02:36.969695940+00:00 123.333333 0.043333
0.065278 2021-01-27 00:03:55.454543910+00:00 132.000000 0.065278
0.091667 2021-01-20 00:05:31.117301709+00:00 120.333333 0.091944
0.113333 2021-01-23 00:06:49.602149679+00:00 161.333333 0.113611
... ... ... ...
23.881389 2021-01-22 23:52:53.219940492+00:00 182.333333 23.881389
23.903056 2021-01-25 23:54:11.704788462+00:00 128.000000 23.903056
23.929444 2021-01-18 23:55:47.367546261+00:00 112.333333 23.929722
23.951389 2021-01-21 23:57:05.852394231+00:00 118.333333 23.951389
23.973056 2021-01-24 23:58:24.337242201+00:00 126.000000 23.973333
t G
t
0.021667 0.021667 1.105198
0.043056 0.043056 1.292240
0.065278 0.065278 1.540036
0.091667 0.091667 1.222375
0.113333 0.113333 1.994650
... ... ...
23.881389 23.881389 1.999995
23.903056 23.903056 1.419478
23.929444 23.929444 1.087873
23.951389 23.951389 1.181691
23.973056 23.973056 1.362543
[1024 rows x 5 columns] cma=<pfun_cma_model.engine.cma.CMASleepWakeModel object at 0x7f1b0036f7d0> popt=array([ 5.25189771e+00, 3.25261494e+03, 8.05064622e-01, 1.44128012e-01,
5.36199106e-06, -2.21182508e-02]) pcov=array([[ 3.20850760e+02, 7.28704681e+04, 4.55994585e-01,
-1.01618765e-01, -1.26469592e+01, -4.93003338e-01],
[ 7.28704681e+04, 3.70697577e+10, -4.27225411e+05,
-1.16332174e+04, -2.24840479e+05, -2.53833986e+05],
[ 4.55994585e-01, -4.27225411e+05, 6.06027153e+00,
1.27139962e-01, 2.88119606e+00, 2.92547162e+00],
[-1.01618765e-01, -1.16332174e+04, 1.27139962e-01,
6.78302164e-03, 7.95648285e-02, 7.96633438e-02],
[-1.26469592e+01, -2.24840479e+05, 2.88119606e+00,
7.95648285e-02, 1.95481373e+00, 1.53938468e+00],
[-4.93003338e-01, -2.53833986e+05, 2.92547162e+00,
7.96633438e-02, 1.53938468e+00, 2.73812026e+00]]) infodict={'message': 'Optimization terminated successfully.', 'error': None, 'ier': 0, 'result': message: CONVERGENCE: RELATIVE REDUCTION OF F <= FACTR*EPSMCH
success: True
status: 0
fun: 71.67492014774733
x: [ 5.252e+00 3.253e+03 8.051e-01 1.441e-01 5.362e-06
-2.212e-02]
nit: 70
jac: [ 1.450e-04 0.000e+00 1.262e-02 8.066e-02 3.604e-03
0.000e+00]
nfev: 686
njev: 98
hess_inv: <6x6 LbfgsInvHessProduct with dtype=float64>} mesg='Optimization terminated successfully.' ier=0 popt_named={'d': np.float64(5.251897714211664), 'taup': np.float64(3252.6149427248142), 'taug': np.float64(0.8050646215473204), 'B': np.float64(0.14412801161625127), 'Cm': np.float64(5.361991056700414e-06), 'toff': np.float64(-0.02211825075866171)} cond=np.float64(81671288781510.69) diag=array([3.20850760e+02, 3.70697577e+10, 6.06027153e+00, 6.78302164e-03,
1.95481373e+00, 2.73812026e+00])
Raw fitted parameters¶
Displays { param_name: type(param_value) } for all fitted parameters in the model result.
In [50]:
import pandas as pd
raw_params = result.cma.params.model_dump()
print('types of raw parameters')
params_types = {k: ( str(type(v)), repr(v)[:100] + '...' ) for k, v in raw_params.items()}
df_params_types = pd.DataFrame.from_dict(
params_types, orient='index', columns=['type', 'repr_head'])
df_params_types.sort_values(by='type', inplace=False)
types of raw parameters
Out[50]:
| type | repr_head | |
|---|---|---|
| seed | <class 'NoneType'> | None... |
| d | <class 'float'> | 5.251897714211664... |
| taup | <class 'float'> | 3252.6149427248142... |
| B | <class 'float'> | 0.14412801161625127... |
| Cm | <class 'float'> | 5.361991056700414e-06... |
| toff | <class 'float'> | -0.02211825075866171... |
| eps | <class 'float'> | 1e-18... |
| N | <class 'int'> | 1024... |
| tM | <class 'list'> | [15.721666666666668, 17.971666666666664, 20.22... |
| taug | <class 'numpy.float64'> | np.float64(0.8050646215473204)... |
CMAModelParams: Pretty Markdown Table¶
Includes qualitative descriptions (e.g., 'Normal', 'Low', 'High')
In [51]:
from pfun_cma_model import CMAModelParams
params = result.cma.params
print(params.generate_markdown_table())
Parameter Type Value Default Lower Bound Upper Bound Description ----------- ------ -------------- --------- ------------- ------------- ------------------------------------------------------- d float 5.2519 0 -12 14 Time zone offset (hours) (High) taup float 3252.61 1 0.5 3 Photoperiod length (hours) (Very High) taug float 0.805065 1 0.1 3 Glucose response time constant (Normal) B float 0.144128 0.05 0 1 Glucose Bias constant (baseline glucose level) (Normal) Cm float 5.36199e-06 0 0 2 Cortisol temporal sensitivity coefficient (Normal) toff float -0.0221183 0 -3 3 Solar noon offset (effects of latitude) (Normal)
Fitted Solution Plotted¶
In [52]:
ax = result.soln.plot(
x='t',
y=['G'],
title='Model Fit Result',
figsize=(10, 5),
linestyle='--',
marker='x',
logy=True
)
result.formatted_data.plot(
x='t',
y=['G'],
ax=ax,
linestyle='',
marker='x',
color='red',
title='Data',
)
Out[52]:
<Axes: title={'center': 'Data'}, xlabel='t'>
Zoomed on dinner estimate (fitted)¶
In [56]:
ax = result.soln.iloc[-200:, :].plot(x='t', y=['G'], figsize=(10, 5), linestyle='-', marker='o')
result.formatted_data.plot(
x='t',
y=['G'],
ax=ax,
linestyle='',
marker='x',
color='red',
title='Model Fit (Dinner Time Detail)',
)
ax.set_xlim(18, 24)
Out[56]:
(18.0, 24.0)
In [54]:
from pfun_cma_model.engine.cma_plot import CMAPlotConfig
cma_plot = CMAPlotConfig()
cma_plot.plot_model_results(
result.formatted_data,
result.soln,
as_blob=False,
separate2subplots=False,
)
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[54], line 5 1 from pfun_cma_model.engine.cma_plot import CMAPlotConfig 3 cma_plot = CMAPlotConfig() ----> 5 cma_plot.plot_model_results( 6 result.formatted_data, 7 result.soln, 8 as_blob=False, 9 separate2subplots=False, 10 ) AttributeError: 'CMAPlotConfig' object has no attribute 'plot_model_results'
In [ ]:
from pfun_cma_model.engine.cma_plot import CMAPlotConfig
cma_plot = CMAPlotConfig()
cma_plot.plot_model_results(
result.formatted_data,
result.soln,
as_blob=False,
separate2subplots=True,
)
Experiments with N=...¶
In [ ]:
for n in [12, 24, 288, 1024]:
print(f'Running model with {n} time points')
soln = pfun.CMASleepWakeModel(N=n).run()
print(soln.shape)
print()
Export notebook to HTML¶
In [ ]:
%%sh
uv run jupyter nbconvert --to html cma-model-modular-demos.ipynb --output cma-model-modular-demos.html
cp cma-model-modular-demos.html ../../pfun_cma_model/static/notebooks/cma-model-modular-demos.html
In [ ]: