Skip to content

Commit

Permalink
Merge pull request #42 from tsalo/refactor
Browse files Browse the repository at this point in the history
[RF] Reorganize package further.
  • Loading branch information
emdupre authored May 13, 2018
2 parents 646c881 + 59d566d commit 8176e80
Show file tree
Hide file tree
Showing 16 changed files with 522 additions and 465 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
# ex: set sts=4 ts=4 sw=4 et:

from .eigendecomp import (
tedpca, tedica, eimask,
tedpca, tedica,
)


__all__ = [
'tedpca', 'tedica', 'eimask'
'tedpca', 'tedica',
]
46 changes: 46 additions & 0 deletions tedana/decomposition/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
Utility functions for tedana decomposition
"""
import logging

import numpy as np
from scipy import stats

logging.basicConfig(format='[%(levelname)s]: %(message)s', level=logging.INFO)
LGR = logging.getLogger(__name__)

F_MAX = 500
Z_MAX = 8


def eimask(dd, ees=None):
"""
Returns mask for data between [0.001, 5] * 98th percentile of dd
Parameters
----------
dd : (S x E x T) array_like
Input data, where `S` is samples, `E` is echos, and `T` is time
ees : (N,) list
Indices of echos to assess from `dd` in calculating output
Returns
-------
imask : (S x N) np.ndarray
Boolean array denoting
"""

if ees is None:
ees = range(dd.shape[1])
imask = np.zeros([dd.shape[0], len(ees)], dtype=bool)
for ee in ees:
LGR.info('++ Creating eimask for echo {}'.format(ee))
perc98 = stats.scoreatpercentile(dd[:, ee, :].flatten(), 98,
interpolation_method='lower')
lthr, hthr = 0.001 * perc98, 5 * perc98
LGR.info('++ Eimask threshold boundaries: '
'{:.03f} {:.03f}'.format(lthr, hthr))
m = dd[:, ee, :].mean(axis=1)
imask[np.logical_and(m > lthr, m < hthr), ee] = True

return imask
Original file line number Diff line number Diff line change
@@ -1,50 +1,24 @@
"""
Signal decomposition methods for tedana
"""
import pickle
import numpy as np
import logging
import os.path as op

import numpy as np
from scipy import stats

from tedana import model, utils
from tedana.decomposition._utils import eimask
from tedana.selection._utils import (getelbow_cons, getelbow_mod)

import logging
logging.basicConfig(format='[%(levelname)s]: %(message)s', level=logging.INFO)
lgr = logging.getLogger(__name__)
LGR = logging.getLogger(__name__)

F_MAX = 500
Z_MAX = 8


def eimask(dd, ees=None):
"""
Returns mask for data between [0.001, 5] * 98th percentile of dd
Parameters
----------
dd : (S x E x T) array_like
Input data, where `S` is samples, `E` is echos, and `T` is time
ees : (N,) list
Indices of echos to assess from `dd` in calculating output
Returns
-------
imask : (S x N) np.ndarray
Boolean array denoting
"""

if ees is None:
ees = range(dd.shape[1])
imask = np.zeros([dd.shape[0], len(ees)], dtype=bool)
for ee in ees:
lgr.info('++ Creating eimask for echo {}'.format(ee))
perc98 = stats.scoreatpercentile(dd[:, ee, :].flatten(), 98,
interpolation_method='lower')
lthr, hthr = 0.001 * perc98, 5 * perc98
lgr.info('++ Eimask threshold boundaries: '
'{:.03f} {:.03f}'.format(lthr, hthr))
m = dd[:, ee, :].mean(axis=1)
imask[np.logical_and(m > lthr, m < hthr), ee] = True

return imask


def tedpca(catd, OCcatd, combmode, mask, t2s, t2sG, stabilize,
ref_img, tes, kdaw, rdaw, ste=0, mlepca=True):
"""
Expand Down Expand Up @@ -94,13 +68,13 @@ def tedpca(catd, OCcatd, combmode, mask, t2s, t2sG, stabilize,
ste = np.array([int(ee) for ee in str(ste).split(',')])

if len(ste) == 1 and ste[0] == -1:
lgr.info('++ Computing PCA of optimally combined multi-echo data')
LGR.info('++ Computing PCA of optimally combined multi-echo data')
d = OCcatd[utils.make_min_mask(OCcatd[:, np.newaxis, :])][:, np.newaxis, :]
elif len(ste) == 1 and ste[0] == 0:
lgr.info('++ Computing PCA of spatially concatenated multi-echo data')
LGR.info('++ Computing PCA of spatially concatenated multi-echo data')
d = catd[mask].astype('float64')
else:
lgr.info('++ Computing PCA of echo #%s' % ','.join([str(ee) for ee in ste]))
LGR.info('++ Computing PCA of echo #%s' % ','.join([str(ee) for ee in ste]))
d = np.stack([catd[mask, ee] for ee in ste - 1], axis=1).astype('float64')

eim = np.squeeze(eimask(d))
Expand All @@ -123,7 +97,7 @@ def tedpca(catd, OCcatd, combmode, mask, t2s, t2sG, stabilize,

# actual variance explained (normalized)
sp = s / s.sum()
eigelb = model.getelbow_mod(sp, val=True)
eigelb = getelbow_mod(sp, val=True)

spdif = np.abs(np.diff(sp))
spdifh = spdif[(len(spdif)//2):]
Expand All @@ -148,17 +122,17 @@ def tedpca(catd, OCcatd, combmode, mask, t2s, t2sG, stabilize,
ctb = np.vstack([ctb.T[:3], sp]).T

# Save state
lgr.info('++ Saving PCA')
LGR.info('++ Saving PCA')
pcastate = {'u': u, 's': s, 'v': v, 'ctb': ctb,
'eigelb': eigelb, 'spmin': spmin, 'spcum': spcum}
try:
with open('pcastate.pkl', 'wb') as handle:
pickle.dump(pcastate, handle)
except TypeError:
lgr.warning('++ Could not save PCA solution.')
LGR.warning('++ Could not save PCA solution.')

else: # if loading existing state
lgr.info('++ Loading PCA')
LGR.info('++ Loading PCA')
with open('pcastate.pkl', 'rb') as handle:
pcastate = pickle.load(handle)
u, s, v = pcastate['u'], pcastate['s'], pcastate['v']
Expand All @@ -171,19 +145,19 @@ def tedpca(catd, OCcatd, combmode, mask, t2s, t2sG, stabilize,
kappas = ctb[ctb[:, 1].argsort(), 1]
rhos = ctb[ctb[:, 2].argsort(), 2]
fmin, fmid, fmax = utils.getfbounds(n_echos)
kappa_thr = np.average(sorted([fmin, model.getelbow_mod(kappas, val=True)/2, fmid]),
kappa_thr = np.average(sorted([fmin, getelbow_mod(kappas, val=True)/2, fmid]),
weights=[kdaw, 1, 1])
rho_thr = np.average(sorted([fmin, model.getelbow_cons(rhos, val=True)/2, fmid]),
rho_thr = np.average(sorted([fmin, getelbow_cons(rhos, val=True)/2, fmid]),
weights=[rdaw, 1, 1])
if int(kdaw) == -1:
kappas_lim = kappas[utils.andb([kappas < fmid, kappas > fmin]) == 2]
kappa_thr = kappas_lim[model.getelbow_mod(kappas_lim)]
kappa_thr = kappas_lim[getelbow_mod(kappas_lim)]
rhos_lim = rhos[utils.andb([rhos < fmid, rhos > fmin]) == 2]
rho_thr = rhos_lim[model.getelbow_mod(rhos_lim)]
rho_thr = rhos_lim[getelbow_mod(rhos_lim)]
stabilize = True
if int(kdaw) != -1 and int(rdaw) == -1:
rhos_lim = rhos[utils.andb([rhos < fmid, rhos > fmin]) == 2]
rho_thr = rhos_lim[model.getelbow_mod(rhos_lim)]
rho_thr = rhos_lim[getelbow_mod(rhos_lim)]

is_hik = np.array(ctb[:, 1] > kappa_thr, dtype=np.int)
is_hir = np.array(ctb[:, 2] > rho_thr, dtype=np.int)
Expand All @@ -202,7 +176,7 @@ def tedpca(catd, OCcatd, combmode, mask, t2s, t2sG, stabilize,
dd = u.dot(np.diag(s*np.array(pcsel, dtype=np.int))).dot(v)

n_components = s[pcsel].shape[0]
lgr.info('++ Selected {0} components. Kappa threshold: {1:.02f}, '
LGR.info('++ Selected {0} components. Kappa threshold: {1:.02f}, '
'Rho threshold: {2:.02f}'.format(n_components, kappa_thr, rho_thr))

dd = stats.zscore(dd.T, axis=0).T # variance normalize timeseries
Expand Down
19 changes: 0 additions & 19 deletions tedana/io/__init__.py

This file was deleted.

28 changes: 0 additions & 28 deletions tedana/io/input_data.py

This file was deleted.

16 changes: 8 additions & 8 deletions tedana/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@
from .fit import (
computefeats2,
fitmodels_direct, get_coeffs,
getelbow_cons, getelbow_mod,
getelbow_aggr, gscontrol_raw,
spatclust,
spatclust, gscontrol_raw,
)

from .combine import (
make_optcom
)

from .t2smap import (
fit, make_optcom, t2sadmap,
from .monoexponential import (
fit_decay, fit_decay_ts
)


__all__ = [
'computefeats2', 'fit', 'fitmodels_direct',
'get_coeffs', 'getelbow_cons', 'getelbow_mod',
'getelbow_aggr', 'gscontrol_raw',
'make_optcom', 'spatclust', 't2sadmap']
'get_coeffs', 'make_optcom', 'spatclust',
'fit_decay', 'fit_decay_ts']
66 changes: 66 additions & 0 deletions tedana/model/combine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
Functions to optimally combine data across echoes.
"""
import logging

import numpy as np

from tedana import utils

logging.basicConfig(format='[%(levelname)s]: %(message)s', level=logging.INFO)
LGR = logging.getLogger(__name__)


def make_optcom(data, t2s, tes, mask, combmode):
"""
Optimally combine BOLD data across TEs.
Parameters
----------
data : (S x E x T) :obj:`numpy.ndarray`
Concatenated BOLD data.
t2 : (S,) :obj:`numpy.ndarray`
Estimated T2* values.
tes : :obj:`numpy.ndarray`
Array of TEs, in seconds.
mask : (S,) :obj:`numpy.ndarray`
Brain mask in 3D array.
combmode : :obj:`str`
How to combine data. Either 'ste' or 't2s'.
useG : :obj:`bool`, optional
Use G. Default is False.
Returns
-------
combined : (S x T) :obj:`numpy.ndarray`
Optimally combined data.
"""

_, _, n_vols = data.shape
mdata = data[mask]
tes = np.array(tes)[np.newaxis] # (1 x E) array_like

if t2s.ndim == 1:
LGR.info('++ Optimally combining data with voxel-wise T2 estimates')
ft2s = t2s[mask, np.newaxis]
else:
LGR.info('++ Optimally combining data with voxel- and volume-wise T2 '
'estimates')
ft2s = t2s[mask, :, np.newaxis]

if combmode == 'ste':
alpha = mdata.mean(axis=-1) * tes
else:
alpha = tes * np.exp(-tes / ft2s)

if t2s.ndim == 1:
alpha = np.tile(alpha[:, :, np.newaxis], (1, 1, n_vols))
else:
alpha = np.swapaxes(alpha, 1, 2)
ax0_idx, ax2_idx = np.where(np.all(alpha == 0, axis=1))
alpha[ax0_idx, :, ax2_idx] = 1.

combined = np.average(mdata, axis=1, weights=alpha)
combined = utils.unmask(combined, mask)

return combined
Loading

0 comments on commit 8176e80

Please sign in to comment.