GLM Fingertapping Example

[1]:
import matplotlib.pyplot as p
import numpy as np
import pandas as pd
import xarray as xr

import cedalion
import cedalion.datasets
import cedalion.io
import cedalion.models.glm as glm
import cedalion.nirs
import cedalion.plots as plots
import cedalion.sigproc.frequency
from cedalion import units

xr.set_options(display_expand_data=False);

Loading and preprocessing the dataset

This notebook uses a finger-tapping dataset in BIDS layout provided by Rob Luke. It can can be downloaded via cedalion.datasets.

[2]:
rec = cedalion.datasets.get_fingertapping()

# rename trials
rec.stim.cd.rename_events(
    {
        "1.0": "control",
        "2.0": "Tapping/Left",
        "3.0": "Tapping/Right",
        "15.0": "sentinel",
    }
)
rec.stim = rec.stim[rec.stim.trial_type != "sentinel"]

# differential pathlenght factors
dpf = xr.DataArray(
    [6, 6],
    dims="wavelength",
    coords={"wavelength": rec["amp"].wavelength},
)

# calculate optical density and concentrations
rec["od"] = cedalion.nirs.int2od(rec["amp"])
rec["conc"] = cedalion.nirs.od2conc(rec["od"], rec.geo3d, dpf, spectrum="prahl")

# Bandpass filter remove cardiac component and slow drifts.
# Here we use a highpass to remove drift. Another possible option would be to
# use drift regressors in the design matrix.
fmin = 0.02 * units.Hz
#fmax = 0.3 * units.Hz
fmax = 0 * units.Hz

rec["conc_filtered"] = cedalion.sigproc.frequency.freq_filter(rec["conc"], fmin, fmax)

display(rec)
<Recording |  timeseries: ['amp', 'od', 'conc', 'conc_filtered'],  masks: [],  stim: ['control', 'Tapping/Left', 'Tapping/Right'],  aux_ts: [],  aux_obj: []>

Plot freq. filtered concentration data for two channels on the left (S1D1, S1D3) and right (S5D5, S5D7) hemispheres.

[3]:
ts = rec["conc_filtered"]

f, ax = p.subplots(4, 1, sharex=True, figsize=(12, 6))
for i, ch in enumerate(["S1D1", "S1D3", "S5D5", "S5D7"]):
    ax[i].plot(ts.time, ts.sel(channel=ch, chromo="HbO"), "r-", label="HbO")
    ax[i].plot(ts.time, ts.sel(channel=ch, chromo="HbR"), "b-", label="HbR")
    ax[i].set_title(f"Ch. {ch}")
    cedalion.plots.plot_stim_markers(ax[i], rec.stim, y=1)
    ax[i].set_ylabel(r"$\Delta$ c / uM")

ax[0].legend(ncol=6)
ax[3].set_label("time / s")
ax[3].set_xlim(0,300)
p.tight_layout()
../../_images/examples_modeling_32_glm_fingertapping_example_5_0.png

Build design matrix

  • use the glm.make_design_matrix method to build regressors

  • to account for signal components from superficial layers use short-distance channel regression: for each long channel the closest short channel is selected. From these the channel-wise regressor’short’ is derived.

[4]:
# split time series into two based on channel distance
ts_long, ts_short = cedalion.nirs.split_long_short_channels(
    rec["conc_filtered"], rec.geo3d, distance_threshold=1.5 * units.cm
)

dms = (
    glm.design_matrix.hrf_regressors(
        ts_long, rec.stim, glm.Gamma(tau=0 * units.s, sigma=3 * units.s, T=3 * units.s)
    )
    & glm.design_matrix.drift_regressors(ts, drift_order=1)
    & glm.design_matrix.closest_short_channel_regressor(ts_long, ts_short, rec.geo3d)
)

The design matrix dm holds all regressors that apply to all channels. It has dimensions ‘time’, ‘chromo’ and ‘regressor’. Regressors have string labels.

[5]:
display(dms)
display(dms.common)

DesignMatrix(universal=['HRF control','HRF Tapping/Left','HRF Tapping/Right','Drift 0','Drift 1'], channel_wise=['short'])
<xarray.DataArray (time: 23239, regressor: 5, chromo: 2)> Size: 2MB
0.0 0.0 0.0 0.0 0.0 0.0 1.0 1.0 4.303e-05 ... 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
Coordinates:
  * time       (time) float64 186kB 0.0 0.128 0.256 ... 2.974e+03 2.974e+03
  * regressor  (regressor) <U17 340B 'HRF control' ... 'Drift 1'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'

channel_wise_regressors is list of additional xr.DataArrays that contain regressors which differ between channels. Each such array may contain only one regressor (i.e. the size of the regressor dimension must be 1). The regressors for each channel are arranged in the additional ‘channel’ dimension.

[6]:

display(dms.channel_wise[0])

<xarray.DataArray 'concentration' (regressor: 1, chromo: 2, channel: 20,
                                   time: 23239)> Size: 7MB
[µM] -0.1671 -0.3522 -0.03758 0.1025 ... 0.06639 -0.001217 0.005855 -0.001648
Coordinates:
  * chromo         (chromo) <U3 24B 'HbO' 'HbR'
  * time           (time) float64 186kB 0.0 0.128 0.256 ... 2.974e+03 2.974e+03
    samples        (time) int64 186kB 0 1 2 3 4 ... 23235 23236 23237 23238
  * channel        (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    short_channel  (channel) object 160B 'S1D9' 'S1D9' ... 'S8D16' 'S8D16'
    comp_group     (channel) int64 160B 0 0 0 1 1 1 2 2 3 3 4 4 4 5 5 5 6 6 7 7
  * regressor      (regressor) <U5 20B 'short'
[7]:
dms.channel_wise[0] = dms.channel_wise[0].pint.dequantify()
dms.channel_wise[0] /= dms.channel_wise[0].max("time")

Visualize the design matrix

[8]:
dm = dms.common
display(dm)

# using xr.DataArray.plot
f, ax = p.subplots(1,1,figsize=(12,5))
dm.sel(chromo="HbO", time=dm.time < 600).T.plot()
p.xticks(rotation=90)
p.show()

# line plots of all regressors
f, ax = p.subplots(2,1,sharex=True, figsize=(12,5))

ch = "S5D5"

for i, chromo in enumerate(["HbO", "HbR"]):
    for reg in dm.regressor.values:
        ax[i].plot(dm.time, dm.sel(chromo=chromo, regressor=reg), label=reg)

    for cwr in dms.channel_wise:
        for reg in cwr.regressor.values:
            ax[i].plot(cwr.time, cwr.sel(chromo=chromo, regressor=reg, channel=ch), label=reg)
    plots.plot_stim_markers(ax[i], rec.stim, y=1)
    ax[i].grid()
    ax[i].set_title(chromo)
    ax[i].set_ylim(-1.5,1.5)

ax[0].legend(ncol=5)
ax[0].set_xlim(0,240);
<xarray.DataArray (time: 23239, regressor: 5, chromo: 2)> Size: 2MB
0.0 0.0 0.0 0.0 0.0 0.0 1.0 1.0 4.303e-05 ... 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
Coordinates:
  * time       (time) float64 186kB 0.0 0.128 0.256 ... 2.974e+03 2.974e+03
  * regressor  (regressor) <U17 340B 'HRF control' ... 'Drift 1'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
../../_images/examples_modeling_32_glm_fingertapping_example_14_1.png
../../_images/examples_modeling_32_glm_fingertapping_example_14_2.png

Fitting the model

[9]:
results = glm.fit(ts_long, dms, noise_model="ar_irls", max_jobs=1)

display(results)

#pd.set_option('display.max_rows', None)
#display(betas.rename("beta").to_dataframe())
<xarray.DataArray (channel: 20, chromo: 2)> Size: 320B
<statsmodels.robust.robust_linear_model.RLMResultsWrapper object at 0x7fe667f...
Coordinates:
  * chromo    (chromo) <U3 24B 'HbO' 'HbR'
  * channel   (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source    (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector  (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS
[10]:
betas = results.sm.params
betas
[10]:
<xarray.DataArray (channel: 20, chromo: 2, regressor: 7)> Size: 2kB
1.986e-07 0.01244 0.2439 0.3886 -0.02322 ... -0.0298 0.02813 1.21e-06 0.09318
Coordinates:
  * regressor  (regressor) object 56B 'index' 'HRF control' ... 'short'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
  * channel    (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source     (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector   (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS
[11]:
betas.rename("betas").to_dataframe()
[11]:
source detector betas
channel chromo regressor
S1D1 HbO index S1 D1 1.986168e-07
HRF control S1 D1 1.244468e-02
HRF Tapping/Left S1 D1 2.439477e-01
HRF Tapping/Right S1 D1 3.886186e-01
Drift 0 S1 D1 -2.321850e-02
... ... ... ... ... ...
S8D8 HbR HRF Tapping/Left S8 D8 -4.158567e-02
HRF Tapping/Right S8 D8 -2.980406e-02
Drift 0 S8 D8 2.812522e-02
Drift 1 S8 D8 1.210228e-06
short S8 D8 9.317619e-02

280 rows × 3 columns

[12]:
# best fit parameters + confidence intervals
pd.concat([results[0,0].item().conf_int(), results[0,0].item().params.rename("beta")], axis=1)
[12]:
0 1 beta
index -0.000004 3.936040e-06 1.986168e-07
HRF control -0.030763 5.565234e-02 1.244468e-02
HRF Tapping/Left 0.200736 2.871596e-01 2.439477e-01
HRF Tapping/Right 0.345392 4.318451e-01 3.886186e-01
Drift 0 -0.025848 -2.058892e-02 -2.321850e-02
Drift 1 -0.000001 -8.860806e-07 -9.991095e-07
short 0.226738 2.462663e-01 2.365021e-01

Model Predictions

  • using glm.predict one can scale the regressors in dm and channel_wise_regressors with the estimated coefficients to obtain a model prediction

  • by giving only a subset of betas to glm.predict one can predict subcomponents of the model

[13]:
# prediction using all regressors
betas = results.sm.params
pred = glm.predict(ts_long, betas, dms)#, channel_wise_regressors)

# prediction of all nuisance regressors, i.e. all regressors that don't start with 'HRF '
pred_wo_hrf = glm.predict(
    ts_long,
    betas.sel(regressor=~betas.regressor.str.startswith("HRF ")),
    dms,
)

# prediction of all HRF regressors, i.e. all regressors that start with 'HRF '
pred_hrf = glm.predict(
    ts_long,
    betas.sel(regressor=betas.regressor.str.startswith("HRF ")),
    dms,
)

Plot model predictions

[14]:
# plot the data and model prediction
#ch = "S6D7"
ch = "S5D5"
f, ax = p.subplots(1,1, figsize=(12, 4))
p.plot(ts_long.time, ts_long.sel(chromo="HbO", channel=ch), "r-", label="data HbO", alpha=.5)
p.plot(pred.time, pred.sel(chromo="HbO", channel=ch), "r-", label="model", lw=2 )
p.plot(pred.time, pred_wo_hrf.sel(chromo="HbO", channel=ch), "k:", label="model w/o HRF", alpha=.5)
plots.plot_stim_markers(ax, rec.stim, y=1)
p.xlim(60,300)
p.ylim(-.4,.4)
p.xlabel("time / s")
p.ylabel(r"$\Delta$  c / uM")
p.legend(ncol=4)


# subtract from data nuisance regressors and plot against predicted HRF components
f, ax = p.subplots(1,1, figsize=(12, 4))
p.plot(pred_hrf.time, pred_hrf.sel(chromo="HbO", channel=ch), "r-", label="HRF HbO")
p.plot(pred_hrf.time, pred_hrf.sel(chromo="HbR", channel=ch), "b-", label="HRF HbR")
p.plot(
    pred_hrf.time,
    ts_long.sel(chromo="HbO", channel=ch).pint.dequantify() - pred_wo_hrf.sel(chromo="HbO", channel=ch),
    "r-", label="data HbO - nuisance reg.", alpha=.5
)
p.plot(
    pred_hrf.time,
    ts_long.sel(chromo="HbR", channel=ch).pint.dequantify() - pred_wo_hrf.sel(chromo="HbR", channel=ch),
    "b-", label="data HbR - nuisance reg.", alpha=.5
)
plots.plot_stim_markers(ax, rec.stim, y=1)
p.legend(ncol=4, loc="lower right")

p.xlim(60,500)
p.xlabel("time / s")
p.ylabel(r"$\Delta$  c / uM");

../../_images/examples_modeling_32_glm_fingertapping_example_23_0.png
../../_images/examples_modeling_32_glm_fingertapping_example_23_1.png

Scalp plots

Betas

[15]:
f, ax = p.subplots(2, 3, figsize=(12, 8))
vlims = {"HbO" : [0.,0.3], "HbR" : [-0.1, 0.05]}
for i_chr, chromo in enumerate(betas.chromo.values):
    vmin, vmax = vlims[chromo]
    for i_reg, reg in enumerate(["HRF Tapping/Left", "HRF Tapping/Right", "HRF control"]):
        cedalion.plots.scalp_plot(
            rec["amp"],
            rec.geo3d,
            betas.sel(chromo=chromo, regressor=reg),
            ax[i_chr, i_reg],
            min_dist=1.5 * cedalion.units.cm,
            title=f"{chromo} {reg}",
            vmin=vmin,
            vmax=vmax,
            optode_labels=True,
            cmap="RdBu_r",
            cb_label=r"$\beta$"
        )
p.tight_layout()
../../_images/examples_modeling_32_glm_fingertapping_example_25_0.png

T-Values

[16]:
display(results.sm.tvalues)
results.sm.tvalues.min().item(), results.sm.tvalues.max().item()
<xarray.DataArray (channel: 20, chromo: 2, regressor: 7)> Size: 2kB
0.1042 0.5645 11.06 17.62 -17.31 -17.32 ... -3.23 -2.315 53.12 53.16 26.0
Coordinates:
  * regressor  (regressor) object 56B 'index' 'HRF control' ... 'short'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
  * channel    (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source     (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector   (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS
[16]:
(-38.69743054194642, 252.5603397409748)
[17]:
f, ax = p.subplots(2, 3, figsize=(12, 8))
vlims = {"HbO" : [-20,20], "HbR" : [-20, 20]}
for i_chr, chromo in enumerate(betas.chromo.values):
    vmin, vmax = vlims[chromo]
    for i_reg, reg in enumerate(["HRF Tapping/Left", "HRF Tapping/Right", "HRF control"]):
        cedalion.plots.scalp_plot(
            rec["amp"],
            rec.geo3d,
            results.sm.tvalues.sel(chromo=chromo, regressor=reg),
            ax[i_chr, i_reg],
            min_dist=1.5 * cedalion.units.cm,
            title=f"{chromo} {reg}",
            vmin=vmin,
            vmax=vmax,
            optode_labels=True,
            cmap="RdBu_r",
            cb_label=r"$t$"
        )
p.tight_layout()
../../_images/examples_modeling_32_glm_fingertapping_example_28_0.png

SM Functionality to Document

[18]:
results.sm.params
[18]:
<xarray.DataArray (channel: 20, chromo: 2, regressor: 7)> Size: 2kB
1.986e-07 0.01244 0.2439 0.3886 -0.02322 ... -0.0298 0.02813 1.21e-06 0.09318
Coordinates:
  * regressor  (regressor) object 56B 'index' 'HRF control' ... 'short'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
  * channel    (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source     (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector   (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS
[19]:
results.sm.conf_int(alpha=0.05)
[19]:
<xarray.DataArray (channel: 20, chromo: 2, regressor: 7, conf_int: 2)> Size: 4kB
-3.539e-06 3.936e-06 -0.03076 0.05565 ... 1.166e-06 1.255e-06 0.08615 0.1002
Coordinates:
  * regressor  (regressor) object 56B 'index' 'HRF control' ... 'short'
  * conf_int   (conf_int) int64 16B 0 1
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
  * channel    (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source     (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector   (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS
[20]:
results.sm.cov_params()
[20]:
<xarray.DataArray (channel: 20, chromo: 2, regressor_r: 7, regressor_c: 7)> Size: 16kB
3.636e-12 -4.555e-10 3.26e-10 3.074e-10 ... -5.246e-07 -2.257e-11 1.284e-05
Coordinates:
  * regressor_r  (regressor_r) object 56B 'index' 'HRF control' ... 'short'
  * regressor_c  (regressor_c) object 56B 'index' 'HRF control' ... 'short'
  * chromo       (chromo) <U3 24B 'HbO' 'HbR'
  * channel      (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source       (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S8' 'S8'
    detector     (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS
[21]:
p.imshow(results.sm.cov_params()[0,0,:,:]);
../../_images/examples_modeling_32_glm_fingertapping_example_33_0.png
[22]:
# convenience function to access the diagonal elements of the cov matrices
results.sm.regressor_variances()
[22]:
<xarray.DataArray (channel: 20, chromo: 2, regressor: 7)> Size: 2kB
3.636e-12 0.000486 0.0004861 0.0004864 ... 2.804e-07 5.182e-16 1.284e-05
Coordinates:
  * regressor  (regressor) object 56B 'index' 'HRF control' ... 'short'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
  * channel    (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source     (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector   (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS

Statistical Tests

t-test statmodels docs

[23]:
results.sm.tvalues
[23]:
<xarray.DataArray (channel: 20, chromo: 2, regressor: 7)> Size: 2kB
0.1042 0.5645 11.06 17.62 -17.31 -17.32 ... -3.23 -2.315 53.12 53.16 26.0
Coordinates:
  * regressor  (regressor) object 56B 'index' 'HRF control' ... 'short'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
  * channel    (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source     (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector   (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS

specifying contrasts through strings results in an array of ContrastResult objects

[24]:
hypotheses = "HRF Tapping/Left = HRF control, HRF Tapping/Right = HRF control"
results.sm.t_test(hypotheses)
[24]:
<xarray.DataArray (channel: 20, chromo: 2)> Size: 320B
                             Test for Constraints                            ...
Coordinates:
  * chromo    (chromo) <U3 24B 'HbO' 'HbR'
  * channel   (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source    (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector  (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Attributes:
    description:  AR_IRLS

extract tvalues and pvalues with map

FIXME: add convenience functions

[25]:
display(results.sm.t_test(hypotheses).sm.map(lambda i : i.tvalue, name="hypothesis"))
<xarray.DataArray (channel: 20, chromo: 2, hypothesis: 2)> Size: 640B
7.464 12.13 -7.041 -9.004 5.186 7.922 ... -2.348 1.474 0.8593 -1.862 -1.202
Coordinates:
  * chromo    (chromo) <U3 24B 'HbO' 'HbR'
  * channel   (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source    (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector  (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Dimensions without coordinates: hypothesis
Attributes:
    description:  AR_IRLS
[26]:
display(results.sm.t_test(hypotheses).sm.map(lambda i : i.pvalue, name="hypothesis"))
<xarray.DataArray (channel: 20, chromo: 2, hypothesis: 2)> Size: 640B
8.389e-14 7.6e-34 1.908e-12 2.176e-19 2.15e-07 ... 0.1406 0.3902 0.06257 0.2295
Coordinates:
  * chromo    (chromo) <U3 24B 'HbO' 'HbR'
  * channel   (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source    (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector  (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Dimensions without coordinates: hypothesis
Attributes:
    description:  AR_IRLS

Plotting Uncertainty Bands

[27]:
betas = results.sm.params
cov = results.sm.cov_params()
[28]:
sampled_betas = xr.zeros_like(betas).expand_dims({"sample" : 100}, axis=-1).copy()
for i_ch in range(sampled_betas.shape[0]):
    for i_cr in range(sampled_betas.shape[1]):
        sampled_betas[i_ch, i_cr, :, :] = np.random.multivariate_normal(
            betas[i_ch, i_cr, :],
            cov[i_ch, i_cr, :, :],
            size=100,
        ).T
[29]:
sampled_betas
[29]:
<xarray.DataArray (channel: 20, chromo: 2, regressor: 7, sample: 100)> Size: 224kB
1.908e-06 1.903e-06 -1.916e-06 -1.118e-06 ... 0.09279 0.09628 0.09014 0.08959
Coordinates:
  * regressor  (regressor) object 56B 'index' 'HRF control' ... 'short'
  * chromo     (chromo) <U3 24B 'HbO' 'HbR'
  * channel    (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    source     (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S7' 'S8' 'S8'
    detector   (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D6' 'D7' 'D7' 'D8'
Dimensions without coordinates: sample
Attributes:
    description:  AR_IRLS
[30]:
pred = glm.predict(ts_long, sampled_betas, dms)
[31]:
pred
[31]:
<xarray.DataArray (time: 23239, channel: 20, chromo: 2, sample: 100)> Size: 744MB
-0.05276 -0.05342 -0.05097 -0.05139 -0.0537 ... 0.02783 0.02785 0.02849 0.02915
Coordinates:
  * time           (time) float64 186kB 0.0 0.128 0.256 ... 2.974e+03 2.974e+03
  * chromo         (chromo) <U3 24B 'HbO' 'HbR'
    samples        (time) int64 186kB 0 1 2 3 4 ... 23235 23236 23237 23238
  * channel        (channel) object 160B 'S1D1' 'S1D2' 'S1D3' ... 'S8D7' 'S8D8'
    short_channel  (channel) object 160B 'S1D9' 'S1D9' ... 'S8D16' 'S8D16'
    comp_group     (channel) int64 160B 0 0 0 1 1 1 2 2 3 3 4 4 4 5 5 5 6 6 7 7
    source         (channel) object 160B 'S1' 'S1' 'S1' 'S2' ... 'S7' 'S8' 'S8'
    detector       (channel) object 160B 'D1' 'D2' 'D3' 'D1' ... 'D7' 'D7' 'D8'
Dimensions without coordinates: sample
[32]:
pred_mean = pred.mean("sample")
pred_std = pred.std("sample")
[33]:
mm = pred_mean.loc[slice(60,80), "S5D5", "HbO"]
ss = pred_std.loc[slice(60,80), "S5D5", "HbO"]

p.plot(mm.time, mm, c="r")
p.fill_between(mm.time, mm-3*ss, mm+3*ss, fc="y", alpha=.8)

p.xlabel("time / s")
p.ylabel(r"$\Delta$  c / uM");
../../_images/examples_modeling_32_glm_fingertapping_example_49_0.png