def ICA(self, mneObj, icCount=None, random_state=None):
        picks = self.createPicks(mneObj)
        reject = dict(eeg=300)

        if icCount is None:
            icCount = len(picks)
        ica = ICA(n_components=icCount,
                  method="fastica",
                  random_state=random_state)
        ica.fit(mneObj, picks=picks, reject=reject)

        return ica
예제 #2
0
def reject_ica(inst,
               reference,
               n_components=0.99,
               method="fastica",
               corr_thresh=0.9,
               random_state=None,
               plot=False):

    if isinstance(reference, str):
        reference = read_ica(reference)

    ica = ICA(n_components=n_components, method=method)
    ica.fit(inst)
    labels = list(reference.labels_.keys())
    components = list(reference.labels_.values())

    for component, label in zip(components, labels):
        corrmap([reference, ica],
                template=(0, component[0]),
                plot=plot,
                label=label,
                threshold=corr_thresh)

    exclude = [item for subl in list(ica.labels_.values()) for item in subl]
    ica.apply(inst, exclude=exclude)

    return inst, ica
예제 #3
0
def runICA(subjectID):
    jumpAmplitudes = dict(grad=400e-12, mag=6e-12)
    subject = 'dh{:#02d}a'.format(subjectID)
    subjectPath = data_path + '/MEG_mc_hp004/' + subject + '/block'
    for block in ['1', '2']:
        outfilename = subjectPath + block + '_ica.fif'
        raw_fname = subjectPath + block + '.fif'
        if os.path.exists(raw_fname):
            raw = Raw(raw_fname, preload=True)
            raw.info['bads'] = badChanLibrary[subjectID][int(block)]
            MEG_channels = mne.fiff.pick_types(raw.info,
                                               meg=True,
                                               eeg=False,
                                               eog=False,
                                               stim=False)
            ica = ICA(n_components=0.99,
                      n_pca_components=64,
                      max_pca_components=100,
                      noise_cov=None,
                      random_state=17259)

            # decompose sources for raw data
            ica.fit(raw, picks=MEG_channels, reject=jumpAmplitudes, tstep=1.0)

            # Save ICA results for diagnosis and reconstruction
            ica.save(outfilename)

        else:
            print(raw_fname + ' does not exist. Skipping ICA.')
예제 #4
0
def ICA_decompose(raw, method, decim, variance, npcas, maxpcas, reject, picks):
    #################### RUN ICA
    ica = ICA(n_components=variance,
              n_pca_components=npcas,
              max_pca_components=maxpcas,
              method=method,
              verbose=True)
    ica.fit(raw, decim=decim, reject=reject, picks=picks)
    ica.get_sources(raw)
    return ica
def preprocess_raw_ica_only(sub_id, session):
    """ This function removes the ICA component that correlates woth the 
    EOG channel(s) best.
    No filtering or downsampling is applied!
    """

    # SETUP AND LOAD FILES ####
    # name with subject id & session name
    fname = "sub_%d_%s" % (sub_id, session)

    # load the raw fif
    print '\nLoading raw file'
    raw = fiff.Raw(fname + "_tsss_mc.fif", preload=True)

    picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=False,
                                stim=False, exclude='bads')

    # ICA ####
    print '\nRun ICA'
    ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100,
              noise_cov=None, random_state=0)

    start, stop = None, None

    # decompose sources for raw data
    ica.decompose_raw(raw, start=start, stop=stop, picks=picks)

    corr = lambda x, y: np.array([pearsonr(a, y.ravel()) for a in x])[:, 0]

    eog_scores_1 = ica.find_sources_raw(raw, target='EOG001',
                                        score_func=corr)
    eog_scores_2 = ica.find_sources_raw(raw, target='EOG002',
                                        score_func=corr)

    # get maximum correlation index for EOG
    eog_source_idx_1 = np.abs(eog_scores_1).argmax()
    eog_source_idx_2 = np.abs(eog_scores_2).argmax()

    # We now add the eog artifacts to the ica.exclusion list
    if eog_source_idx_1 ==  eog_source_idx_2:
        ica.exclude += [eog_source_idx_1]

    elif eog_source_idx_1 !=  eog_source_idx_2:
        ica.exclude += [eog_source_idx_1, eog_source_idx_2]

    print eog_source_idx_1, eog_source_idx_2
    print ica.exclude

    # Restore sensor space data
    raw_ica = ica.pick_sources_raw(raw, include=None)

    # SAVE FILES ####
    raw_ica.save(fname + '_tsss_mc_preproc_ica.fif', overwrite=True)
예제 #6
0
def ICA_decompose(raw, method, decim, variance, npcas, maxpcas, reject, picks):
    r = np.random.RandomState(1234)  # allow for reproducible results
    r.uniform(0, 10, 5)
    #################### RUN ICA
    ica = ICA(n_components=variance,
              n_pca_components=npcas,
              max_pca_components=maxpcas,
              method=method,
              verbose=True,
              random_state=r)
    ica.fit(raw, decim=decim, reject=reject, picks=picks)
    ica.get_sources(raw)
    return ica
def preprocess_raw(sub_id, session):
    """ This function preprocessess data
    """

    # SETUP AND LOAD FILES ####
    # name with subject id & session name
    fname = "sub_%d_%s" % (sub_id, session)

    # load the raw fif
    print '\nLoading raw file'
    raw = fiff.Raw(fname + "_tsss_mc.fif", preload=True)

    picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=False,
                                stim=False, exclude='bads')

    print 'Computing Covariance matrix'
    cov = mne.compute_raw_data_covariance(raw, picks=picks, reject=None)

    # FILTER ####
    # filter raw, lp 128, bp at 50 & 100
    raw.filter(None, 128, n_jobs=n_jobs, verbose=True)

#    steps = np.arange(50, 151, 50)
#    print '\nBand stop filter at %s' % steps
#    raw.notch_filter(steps, n_jobs=n_jobs, verbose=True)

    # ICA ####
    print '\nRun ICA'
    ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100,
              noise_cov=None, random_state=0)

    start, stop = None, None

    # decompose sources for raw data
    ica.decompose_raw(raw, start=start, stop=stop, picks=picks)

    corr = lambda x, y: np.array([pearsonr(a, y.ravel()) for a in x])[:, 0]

    eog_scores_1 = ica.find_sources_raw(raw, target='EOG001',
                                        score_func=corr)
    eog_scores_2 = ica.find_sources_raw(raw, target='EOG002',
                                        score_func=corr)

    # get maximum correlation index for EOG
    eog_source_idx_1 = np.abs(eog_scores_1).argmax()
    eog_source_idx_2 = np.abs(eog_scores_2).argmax()

    # We now add the eog artifacts to the ica.exclusion list
    if eog_source_idx_1 == eog_source_idx_2:
        ica.exclude += [eog_source_idx_1]
    elif eog_source_idx_1 != eog_source_idx_2:
        ica.exclude += [eog_source_idx_1, eog_source_idx_2]

    print eog_source_idx_1, eog_source_idx_2
    print ica.exclude

    # Restore sensor space data
    raw_ica = ica.pick_sources_raw(raw, include=None)

    # EPOCHS ####
    events = mne.find_events(raw_ica, stim_channel="STI101")
    events_classic = []
    events_interupt = []
    for i in range(len(events)):
        if i > 0:
            if events[i, 2] == 1 and events[i - 1, 2] == 1:
                events_classic.append(i)
            elif events[i, 2] == 1 and events[i - 1, 2] == 2:
                events_interupt.append(i)

    picks = mne.fiff.pick_types(raw_ica.info, meg=True, eeg=False, eog=False,
                                emg=True, stim=False, exclude='bads')

    reject = dict(grad=4000e-13)
    epochs = mne.Epochs(raw_ica, events[events_classic], event_id, tmin, tmax,
                        proj=True, picks=picks, baseline=baseline,
                        preload=False, reject=reject)

    # SAVE FILES ####
    raw_ica.save(fname + '_tsss_mc_ica.fif', overwrite=True)
    cov.save((fname + '_tsss_mc_cov.fif'))
    epochs.save(fname + '_tsss_mc_ica_epochs.fif')
예제 #8
0
raw = Raw(raw_fname, preload=True)

picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=False,
                            stim=False, exclude='bads')

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Instead of the actual number of components here we pass a float value
# between 0 and 1 to select n_components by a percentage of
# explained variance. Also we decide to use 64 PCA components before mixing
# back to sensor space. These include the PCA components supplied to ICA plus
# additional PCA components up to rank 64 of the MEG data.
# This allows to control the trade-off between denoising and preserving signal.

ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100,
          noise_cov=None, random_state=0)
print ica

# 1 minute exposure should be sufficient for artifact detection.
# However, rejection performance may significantly improve when using
# the entire data range
start, stop = raw.time_as_index([100, 160])

# decompose sources for raw data
ica.decompose_raw(raw, start=start, stop=stop, picks=picks)
print ica

sources = ica.get_sources_raw(raw, start=start, stop=stop)

# setup reasonable time window for inspection
start_plot, stop_plot = raw.time_as_index([100, 103])
예제 #9
0
def eeglab2mne(fname, montage='standard_1020', event_id=None, load_ica=False):
    """Get an EEGLAB dataset into a MNE Raw object.

    Parameters
    ----------
    input_fname : str
        Path to the .set file. If the data is stored in a separate .fdt file,
        it is expected to be in the same folder as the .set file.
    montage : str | None | instance of montage
        Path or instance of montage containing electrode positions.
        If None, sensor locations are (0,0,0). See the documentation of
        :func:`mne.channels.read_montage` for more information.
    event_id : dict
        Map event id (integer) to string name of corresponding event.
        This allows to smoothly load EEGLAB event structure when overlapping
        events (e.g. boundaries) occur.
    load_ica : bool
        Default to False. Load ica matrices from eeglab structure if available
        and attempt to transfer them into the ica structure of MNE.

    Returns
    -------
    raw : Instance of RawEEGLAB
        A Raw object containing EEGLAB .set data.
    ica : Instance of ICA
        If load_ica True

    Note
    ----
    ICA matrices in ICA MNE object might not entirely capture the decomposition.
    To apply projections (i.e. remove some components from observed EEG data) it
    might be better to load directly the matrices and do it by hand, where:

        - icawinv = pinv(icaweights * icasphere)
        - ica_act = icaweights * icasphere * eegdata

    References
    ----------
    .. [#] https://benediktehinger.de/blog/science/ica-weights-and-invweights/
    .. [#] https://github.com/mne-tools/mne-python/pull/5114/files
    """
    montage_mne = mne.channels.make_standard_montage(montage)

    try:
        raw = mne.io.read_raw_eeglab(input_fname=fname, preload=True)
    except NotImplementedError:
        print("Version 7.3 matlab file detected, will load 'by hand'")
        eeg, srateate, _, _, _, ch_names = _load_eeglab_data(fname)
        info = mne.create_info(ch_names=ch_names,
                               sfreq=srateate,
                               ch_types='eeg')
        raw = mne.io.RawArray(eeg.T, info)
    # set up montage:
    raw.set_montage(montage_mne)

    if load_ica:
        weights, winv, sphere = load_ica_matrices(fname)
        ica = ICA(n_components=winv.shape[1], max_pca_components=winv.shape[1])
        ica.fit(raw, decim=2, start=1., stop=60.)
        ica.unmixing_matrix_ = weights.dot(sphere.dot(ica.pca_components_.T))
        ica.mixing_matrix_ = np.linalg.pinv(ica.unmixing_matrix_)

        return raw, ica
    return raw
예제 #10
0
raw = Raw(raw_fname, preload=True)

picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=False,
                            stim=False, exclude='bads')

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Instead of the actual number of components here we pass a float value
# between 0 and 1 to select n_components by a percentage of
# explained variance. Also we decide to use 64 PCA components before mixing
# back to sensor space. These include the PCA components supplied to ICA plus
# additional PCA components up to rank 64 of the MEG data.
# This allows to control the trade-off between denoising and preserving signal.

ica = ICA(n_components=0.90, n_pca_components=None, max_pca_components=100,
          random_state=0)

# 1 minute exposure should be sufficient for artifact detection.
# However, rejection performance may significantly improve when using
# the entire data range

# decompose sources for raw data using each third sample.
ica.decompose_raw(raw, picks=picks, decim=3)
print ica

# plot reasonable time window for inspection
start_plot, stop_plot = 100., 103.
ica.plot_sources_raw(raw, range(30), start=start_plot, stop=stop_plot)

###############################################################################
# Automatically find the ECG component using correlation with ECG signal.
예제 #11
0
def ica_convert2mne(unmixing, pca, info=None, method='fastica'):

    # create MNE-type of ICA object
    from mne.preprocessing.ica import ICA
    n_comp = unmixing.shape[1]

    if method == 'extended-infomax':
        ica_method = 'infomax'
        fit_params = dict(extended=True)
    else:
        ica_method = method
        fit_params = None

    ica = ICA(n_components=n_comp, method=ica_method, fit_params=fit_params)

    # add PCA object
    ica.pca = pca

    # PCA info to be used bei MNE-Python
    ica.pca_mean_ = pca.mean_
    ica.pca_components_ = pca.components_
    exp_var = pca.explained_variance_
    ica.pca_explained_variance_ = exp_var
    ica.pca_explained_variance_ratio_ = pca.explained_variance_ratio_

    # ICA info
    ica.n_components_ = n_comp
    ica.n_components = n_comp
    ica.components_ = unmixing  # compatible with sklearn
    ica.unmixing_ = ica.components_  # as used by sklearn
    ica.mixing_ = pinv(ica.unmixing_)  # as used by sklearn
    ica.unmixing_matrix_ = ica.unmixing_ / np.sqrt(
        exp_var[0:n_comp])[None, :]  # as used by MNE-Python
    ica.mixing_matrix_ = pinv(ica.unmixing_matrix_)  # as used by MNE-Python
    ica._ica_names = ['ICA%03d' % ii for ii in range(n_comp)]
    ica.fun = method
    if info:
        ica.info = info

    return ica
예제 #12
0
                             fhigh,
                             method='fir',
                             n_jobs=2,
                             fir_design='firwin',
                             phase='zero')

# use 60s of data
raw_filt.crop(0, 60)
raw.crop(0, 60)
raw_unfilt = raw.copy()

picks = mne.pick_types(raw.info, meg=True, exclude='bads')

ica_mne = ICA_mne(method='fastica',
                  n_components=60,
                  random_state=42,
                  max_pca_components=None,
                  max_iter=5000,
                  verbose=False)

# fit ica object from mne to filtered data
ica_mne.fit(raw_filt, picks=picks, reject=reject, verbose=True)

# save mean and standard deviation of filtered MEG channels for the standard mne routine
pca_mean_filt_mne = ica_mne.pca_mean_.copy()
pca_pre_whitener_filt_mne = ica_mne._pre_whitener.copy(
)  # this is the standard deviation of MEG channels

ica_jumeg = ICA_jumeg(method='fastica',
                      n_components=60,
                      random_state=42,
                      max_pca_components=None,
예제 #13
0
raw = Raw(raw_fname, preload=True)

picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=True, ecg=True, stim=False, exclude="bads")

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Instead of the actual number of components here we pass a float value
# between 0 and 1 to select n_components by a percentage of
# explained variance. Also we decide to use 64 PCA components before mixing
# back to sensor space. These include the PCA components supplied to ICA plus
# additional PCA components up to rank 64 of the MEG data.
# This allows to control the trade-off between denoising and preserving signal.

ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100, noise_cov=None, random_state=0)
print ica

# get epochs
tmin, tmax, event_id = -0.2, 0.5, 1
# baseline = None
baseline = (None, 0)
reject = None

events = mne.find_events(raw, stim_channel="STI 014")
epochs = mne.Epochs(
    raw, events, event_id, tmin, tmax, proj=True, picks=picks, baseline=baseline, preload=True, reject=reject
)


# fit sources from epochs or from raw (both works for epochs)
                            ecg=True, stim=False, exclude='bads')

tmin, tmax, event_id = -0.2, 0.5, 1
baseline = (None, 0)
reject = None

events = mne.find_events(raw, stim_channel='STI 014')
epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=False, picks=picks,
                    baseline=baseline, preload=True, reject=reject)

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.
# for more background information visit the plot_ica_from_raw.py example

# fit sources from epochs or from raw (both works for epochs)
ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100,
          noise_cov=None, random_state=0)

ica.decompose_epochs(epochs)
print ica

###############################################################################
# Automatically find ECG and EOG component using correlation coefficient.

# As we don't have an ECG channel we use one that correlates a lot with heart
# beats: 'MEG 1531'. We can directly pass the name to the find_sources method.
# In our example, the find_sources method returns and array of correlation
# scores for each ICA source.

ecg_scores = ica.find_sources_epochs(epochs, target='MEG 1531',
                                     score_func='pearsonr')
예제 #15
0
tmin, tmax, event_id = -0.2, 0.5, 1
baseline = (None, 0)
reject = None

events = mne.find_events(raw, stim_channel='STI 014')
epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=False, picks=picks,
                    baseline=baseline, preload=True, reject=reject)

random_state = np.random.RandomState(42)

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.
# for more background information visit the plot_ica_from_raw.py example

# fit sources from epochs or from raw (both works for epochs)
ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100,
          noise_cov=None, random_state=random_state)

ica.decompose_epochs(epochs, decim=2)
print(ica)

# plot spatial sensitivities of a few ICA components
title = 'Spatial patterns of ICA components (Magnetometers)'
source_idx = range(35, 50)
ica.plot_topomap(source_idx, ch_type='mag')
plt.suptitle(title, fontsize=12)


###############################################################################
# Automatically find ECG and EOG component using correlation coefficient.

# As we don't have an ECG channel we use one that correlates a lot with heart
raw = Raw(raw_fname, preload=True)

picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=False,
                            stim=False, exclude='bads')

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Sign and order of components is non deterministic.
# setting the random state to 0 makes the solution reproducible.
# Instead of the actual number of components we pass a float value
# between 0 and 1 to select n_components by a percentage of
# explained variance.

ica = ICA(n_components=0.90, max_pca_components=100, noise_cov=None,
          random_state=0)

# For maximum rejection performance we will compute the decomposition on
# the entire time range

# decompose sources for raw data, select n_components by explained variance
ica.decompose_raw(raw, start=None, stop=None, picks=picks)
print ica

sources = ica.get_sources_raw(raw)

# setup reasonable time window for inspection
start_plot, stop_plot = raw.time_as_index([100, 103])

# plot components
ica.plot_sources_raw(raw, start=start_plot, stop=stop_plot)
예제 #17
0
                            stim=False,
                            exclude='bads')

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Instead of the actual number of components here we pass a float value
# between 0 and 1 to select n_components by a percentage of
# explained variance. Also we decide to use 64 PCA components before mixing
# back to sensor space. These include the PCA components supplied to ICA plus
# additional PCA components up to rank 64 of the MEG data.
# This allows to control the trade-off between denoising and preserving signal.

ica = ICA(n_components=0.90,
          n_pca_components=64,
          max_pca_components=100,
          noise_cov=None,
          random_state=0)
print ica

# 1 minute exposure should be sufficient for artifact detection.
# However, rejection performance may significantly improve when using
# the entire data range
start, stop = raw.time_as_index([100, 160])

# decompose sources for raw data
ica.decompose_raw(raw, start=start, stop=stop, picks=picks)
print ica

sources = ica.get_sources_raw(raw, start=start, stop=stop)
예제 #18
0
                    proj=False,
                    picks=picks,
                    baseline=baseline,
                    preload=True,
                    reject=reject)

random_state = np.random.RandomState(42)

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.
# for more background information visit the plot_ica_from_raw.py example

# fit sources from epochs or from raw (both works for epochs)
ica = ICA(n_components=0.90,
          n_pca_components=64,
          max_pca_components=100,
          noise_cov=None,
          random_state=random_state)

ica.decompose_epochs(epochs, decim=2)
print(ica)

# plot spatial sensitivities of a few ICA components
title = 'Spatial patterns of ICA components (Magnetometers)'
source_idx = range(35, 50)
ica.plot_topomap(source_idx, ch_type='mag')
plt.suptitle(title, fontsize=12)

###############################################################################
# Automatically find ECG and EOG component using correlation coefficient.
예제 #19
0
raw = Raw(raw_fname, preload=True)

picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=True,
                            ecg=True, stim=False, exclude='bads')

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Instead of the actual number of components here we pass a float value
# between 0 and 1 to select n_components by a percentage of
# explained variance. Also we decide to use 64 PCA components before mixing
# back to sensor space. These include the PCA components supplied to ICA plus
# additional PCA components up to rank 64 of the MEG data.
# This allows to control the trade-off between denoising and preserving signal.

ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100,
          noise_cov=None, random_state=0)
print ica

# get epochs
tmin, tmax, event_id = -0.2, 0.5, 1
# baseline = None
baseline = (None, 0)
reject = None

events = mne.find_events(raw, stim_channel='STI 014')
epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, picks=picks,
                    baseline=baseline, preload=True, reject=reject)


# fit sources from epochs or from raw (both works for epochs)
ica.decompose_epochs(epochs)
예제 #20
0
picks = mne.pick_types(raw.info,
                       meg=True,
                       eeg=False,
                       eog=False,
                       stim=False,
                       exclude='bads')

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Instead of the actual number of components here we pass a float value
# between 0 and 1 to select n_components based on the percentage of
# variance explained by the PCA components.

ica = ICA(n_components=0.90,
          n_pca_components=None,
          max_pca_components=None,
          random_state=0)

# Also we decide to use all PCA components before mixing back to sensor space.
# You can again use percentages (float) or set the total number of components
# to be kept directly (int) which allows to control the amount of additional
# denoising.

ica.n_pca_components = 1.0

# decompose sources for raw data using each third sample.
ica.decompose_raw(raw, picks=picks, decim=3)
print(ica)

# plot reasonable time window for inspection
start_plot, stop_plot = 100., 103.
예제 #21
0
raw = Raw(raw_fname, preload=True)

picks = mne.fiff.pick_types(raw.info, meg=True, eeg=False, eog=True,
                            ecg=True, stim=False, exclude='bads')

###############################################################################
# Setup ICA seed decompose data, then access and plot sources.

# Instead of the actual number of components here we pass a float value
# between 0 and 1 to select n_components by a percentage of
# explained variance. Also we decide to use 64 PCA components before mixing
# back to sensor space. These include the PCA components supplied to ICA plus
# additional PCA components up to rank 64 of the MEG data.
# This allows to control the trade-off between denoising and preserving signal.

ica = ICA(n_components=0.90, n_pca_components=64, max_pca_components=100,
          noise_cov=None, random_state=0)
print ica

# get epochs
tmin, tmax, event_id = -0.2, 0.5, 1
# baseline = None
baseline = (None, 0)
reject = None

events = mne.find_events(raw, stim_channel='STI 014')
epochs = mne.Epochs(raw, events, event_id, tmin, tmax, proj=True, picks=picks,
                    baseline=baseline, preload=True, reject=reject)


# fit sources from epochs or from raw (both works for epochs)
ica.decompose_epochs(epochs)