From aa1ba7c996d0c19a5fe6fcdb41981ff5d2a2f421 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 23 Feb 2021 16:18:17 -0500 Subject: [PATCH 01/39] Covers non-verbose non-global mod --- tedana/decomposition/pca.py | 2 +- tedana/io.py | 147 +++++++++++++++++++++++++++--------- tedana/workflows/tedana.py | 48 ++++++++---- 3 files changed, 146 insertions(+), 51 deletions(-) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 27ea23af3..abc561580 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -245,7 +245,7 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, # write component maps to 4D image comp_ts_z = stats.zscore(comp_ts, axis=0) comp_maps = utils.unmask(computefeats2(data_oc, comp_ts_z, mask), mask) - io.filewrite(comp_maps, op.join(out_dir, 'pca_components.nii.gz'), ref_img) + io.filewrite(comp_maps, 'PCA components', ref_img) # Select components using decision tree if algorithm == 'kundu': diff --git a/tedana/io.py b/tedana/io.py index 8e5e70c5b..98603ce44 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -4,6 +4,7 @@ import json import logging import os.path as op +from enum import Enum, unique import numpy as np import pandas as pd @@ -19,6 +20,86 @@ RepLGR = logging.getLogger('REPORT') RefLGR = logging.getLogger('REFERENCES') +outdir = '.' +prefix = '' +convention = 'kundu' + + +img_table = { + 'adaptive mask': ('adaptive_mask', 'desc-adaptiveGoodSignal_mask'), + 't2star map': ('t2sv', 'T2starmap'), + 's0 map': ('s0v', 'S0map'), + 'combined': ('ts_OC', 'desc-optcom_bold'), + 'PCA components': ('pca_components', 'desc-PCA_components'), + 'ICA components': ('betas_OC', 'desc-ICA_components'), + 'ICA accepted components': ('betas_hik_OC', + 'desc-ICAAccepted_components'), + 'z-scored ICA accepted components': ( + 'feats_OC2', + 'desc-ICAAccepted_stat-z_components'), + 'denoised ts': ('dn_ts_OC', 'desc-optcomDenoised_bold'), + 'high kappa ts': ('hik_ts_OC', 'desc-optcomAccepted_bold'), + 'low kappa ts': ('lowk_ts_OC', 'desc-optcomRejected_bold'), + # verbose outputs + 'full t2star map': ('t2svG', 'desc-full_T2starmap'), + 'full s0 map': ('s0vG', 'desc-full_S0map'), + 'whitened': ('ts_OC_whitened', 'desc-optcomPCAReduced_bold'), + 'high kappa ts split': ('hik_ts_e{0}', + 'echo_{0}_desc-Accepted_bold'), + 'low kappa ts split': ('lowk_ts_e{0}', + 'echo_{0}_desc-Rejected_bold'), + 'denoised ts split': ('dn_ts_e{0}', 'echo_{0}_desc-Denoised_bold'), +} + + +def gen_img_name(img_type, echo=0): + """ + Generates an image file full path to simplify file output + + Parameters + ---------- + img_type : str + The description of the image. Must be a key in io.img_table + echo : :obj: `int` + The echo number of the image. + + Returns + ------- + The full path for the image name + + Raises + ------ + KeyError, if an invalid description is supplied + RuntimeError, if io has no convention set + ValueError, if an echo is supplied when it shouldn't be + + See Also + -------- + io.img_table, a dict for translating various naming types + """ + + if convention == 'kundu': + tuple_idx = 0 + elif convention == 'bids': + tuple_idx = 1 + else: + if convention: + raise RuntimeError('Naming convention %s not supported' % + convention + ) + else: + raise RuntimeError('No naming convention given!') + if echo: + img_type += ' split' + format_string = img_table[img_type][tuple_idx] + if echo and not ('{' in format_string): + raise ValueError('Echo supplied when not supported!') + elif echo: + basename = format_string.format(echo) + else: + basename = format_string + return op.join(outdir, prefix + basename) + def split_ts(data, mmix, mask, comptable): """ @@ -60,7 +141,7 @@ def split_ts(data, mmix, mask, comptable): return hikts, resid -def write_split_ts(data, mmix, mask, comptable, ref_img, out_dir='.', suffix=''): +def write_split_ts(data, mmix, mask, comptable, ref_img, echo=0): """ Splits `data` into denoised / noise / ignored time series and saves to disk @@ -77,8 +158,9 @@ def write_split_ts(data, mmix, mask, comptable, ref_img, out_dir='.', suffix='') Reference image to dictate how outputs are saved to disk out_dir : :obj:`str`, optional Output directory. - suffix : :obj:`str`, optional - Appended to name of saved files (before extension). Default: '' + echo: :obj: `int`, optional + Echo number to generate filenames, used by some verbose + functions. Default 0. Returns ------- @@ -117,22 +199,27 @@ def write_split_ts(data, mmix, mask, comptable, ref_img, out_dir='.', suffix='') dnts = data[mask] - lowkts if len(acc) != 0: - fout = filewrite(utils.unmask(hikts, mask), - op.join(out_dir, 'hik_ts_{0}'.format(suffix)), ref_img) + fout = filewrite( + utils.unmask(hikts, mask), 'high kappa ts', ref_img, + echo=echo + ) LGR.info('Writing high-Kappa time series: {}'.format(op.abspath(fout))) if len(rej) != 0: - fout = filewrite(utils.unmask(lowkts, mask), - op.join(out_dir, 'lowk_ts_{0}'.format(suffix)), ref_img) + fout = filewrite( + utils.unmask(lowkts, mask), 'low kappa ts', ref_img, + echo=echo + ) LGR.info('Writing low-Kappa time series: {}'.format(op.abspath(fout))) - fout = filewrite(utils.unmask(dnts, mask), - op.join(out_dir, 'dn_ts_{0}'.format(suffix)), ref_img) + fout = filewrite( + utils.unmask(dnts, mask), 'denoised ts', ref_img, echo=echo + ) LGR.info('Writing denoised time series: {}'.format(op.abspath(fout))) return varexpl -def writefeats(data, mmix, mask, ref_img, out_dir='.', suffix=''): +def writefeats(data, mmix, mask, ref_img): """ Converts `data` to component space with `mmix` and saves to disk @@ -147,10 +234,6 @@ def writefeats(data, mmix, mask, ref_img, out_dir='.', suffix=''): Boolean mask array ref_img : :obj:`str` or img_like Reference image to dictate how outputs are saved to disk - out_dir : :obj:`str`, optional - Output directory. - suffix : :obj:`str`, optional - Appended to name of saved files (before extension). Default: '' Returns ------- @@ -170,11 +253,11 @@ def writefeats(data, mmix, mask, ref_img, out_dir='.', suffix=''): # write feature versions of components feats = utils.unmask(computefeats2(data, mmix, mask), mask) - fname = filewrite(feats, op.join(out_dir, 'feats_{0}'.format(suffix)), ref_img) + fname = filewrite(feats, 'z-scored ICA accepted components', ref_img) return fname -def writeresults(ts, mask, comptable, mmix, n_vols, ref_img, out_dir='.'): +def writeresults(ts, mask, comptable, mmix, n_vols, ref_img): """ Denoises `ts` and saves all resulting files to disk @@ -195,8 +278,6 @@ def writeresults(ts, mask, comptable, mmix, n_vols, ref_img, out_dir='.'): Number of volumes in original time series ref_img : :obj:`str` or img_like Reference image to dictate how outputs are saved to disk - out_dir : :obj:`str`, optional - Output directory. Notes ----- @@ -222,25 +303,24 @@ def writeresults(ts, mask, comptable, mmix, n_vols, ref_img, out_dir='.'): """ acc = comptable[comptable.classification == 'accepted'].index.values - fout = filewrite(ts, op.join(out_dir, 'ts_OC'), ref_img) + fout = filewrite(ts, 'combined', ref_img) LGR.info('Writing optimally combined time series: {}'.format(op.abspath(fout))) - write_split_ts(ts, mmix, mask, comptable, ref_img, out_dir=out_dir, suffix='OC') + write_split_ts(ts, mmix, mask, comptable, ref_img) ts_B = get_coeffs(ts, mmix, mask) - fout = filewrite(ts_B, op.join(out_dir, 'betas_OC'), ref_img) + fout = filewrite(ts_B, 'ICA components', ref_img) LGR.info('Writing full ICA coefficient feature set: {}'.format(op.abspath(fout))) if len(acc) != 0: - fout = filewrite(ts_B[:, acc], op.join(out_dir, 'betas_hik_OC'), ref_img) + fout = filewrite(ts_B[:, acc], 'ICA accepted components', ref_img) LGR.info('Writing denoised ICA coefficient feature set: {}'.format(op.abspath(fout))) fout = writefeats(split_ts(ts, mmix, mask, comptable)[0], - mmix[:, acc], mask, ref_img, out_dir=out_dir, - suffix='OC2') + mmix[:, acc], mask, ref_img) LGR.info('Writing Z-normalized spatial component maps: {}'.format(op.abspath(fout))) -def writeresults_echoes(catd, mmix, mask, comptable, ref_img, out_dir='.'): +def writeresults_echoes(catd, mmix, mask, comptable, ref_img): """ Saves individually denoised echos to disk @@ -258,8 +338,6 @@ def writeresults_echoes(catd, mmix, mask, comptable, ref_img, out_dir='.'): each metric. The index should be the component number. ref_img : :obj:`str` or img_like Reference image to dictate how outputs are saved to disk - out_dir : :obj:`str`, optional - Output directory. Notes ----- @@ -285,8 +363,9 @@ def writeresults_echoes(catd, mmix, mask, comptable, ref_img, out_dir='.'): for i_echo in range(catd.shape[1]): LGR.info('Writing Kappa-filtered echo #{:01d} timeseries'.format(i_echo + 1)) - write_split_ts(catd[:, i_echo, :], mmix, mask, comptable, ref_img, - out_dir=out_dir, suffix='e%i' % (i_echo + 1)) + write_split_ts(catd[:, i_echo, :], mmix, mask, comptable, + ref_img, echo=(i_echo + 1) + ) def new_nii_like(ref_img, data, affine=None, copy_header=True): @@ -325,7 +404,8 @@ def new_nii_like(ref_img, data, affine=None, copy_header=True): return nii -def filewrite(data, filename, ref_img, gzip=True, copy_header=True): +def filewrite(data, img_type, ref_img, gzip=True, copy_header=True, + echo=0): """ Writes `data` to `filename` in format of `ref_img` @@ -342,6 +422,8 @@ def filewrite(data, filename, ref_img, gzip=True, copy_header=True): if output dtype is NIFTI. Default: True copy_header : :obj:`bool`, optional Whether to copy header from `ref_img` to new image. Default: True + echo : :obj: `int`, optional + Indicate the echo index of the data being written. Returns ------- @@ -358,10 +440,7 @@ def filewrite(data, filename, ref_img, gzip=True, copy_header=True): # FIXME: we only handle writing to nifti right now # get root of desired output file and save as nifti image - root = op.dirname(filename) - base = op.basename(filename) - base, ext, add = splitext_addext(base) - root = op.join(root, base) + root = gen_img_name(img_type, echo=echo) name = '{}.{}'.format(root, 'nii.gz' if gzip else 'nii') out.to_filename(name) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 0b4d7c15f..0c96ce529 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -77,6 +77,18 @@ def _get_parser(): "function will be used to derive a mask " "from the first echo's data."), default=None) + optional.add_argument('--prefix', + dest='prefix', + type=str, + help="Prefix for filenames generated.", + default='') + optional.add_argument('--convention', + dest='convention', + action='store', + choices=['kundu', 'bids'], + help=("Filenaming convention. bids will use " + "the latest BIDS derivatives version."), + default='kundu') optional.add_argument('--fittype', dest='fittype', action='store', @@ -235,6 +247,7 @@ def _get_parser(): def tedana_workflow(data, tes, out_dir='.', mask=None, + convention='kundu', prefix='', fittype='loglin', combmode='t2s', tedpca='mdl', fixed_seed=42, maxit=500, maxrestart=10, tedort=False, gscontrol=None, @@ -330,6 +343,12 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, if not op.isdir(out_dir): os.mkdir(out_dir) + io.outdir = out_dir + if prefix and prefix[-1] != '_': + prefix += '_' + io.prefix = prefix + io.convention = convention + # boilerplate basename = 'report' extension = 'txt' @@ -477,7 +496,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, # Create an adaptive mask with at least 3 good echoes. mask, masksum = utils.make_adaptive_mask(catd, mask=mask, getsum=True, threshold=3) LGR.debug('Retaining {}/{} samples'.format(mask.sum(), n_samp)) - io.filewrite(masksum, op.join(out_dir, 'adaptive_mask.nii'), ref_img) + io.filewrite(masksum, 'adaptive mask', ref_img) if t2smap is None: LGR.info('Computing T2* map') @@ -491,12 +510,15 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, LGR.debug('Setting cap on T2* map at {:.5f}s'.format( utils.millisec2sec(cap_t2s))) t2s_limited[t2s_limited > cap_t2s * 10] = cap_t2s - io.filewrite(utils.millisec2sec(t2s_limited), op.join(out_dir, 't2sv.nii'), ref_img) - io.filewrite(s0_limited, op.join(out_dir, 's0v.nii'), ref_img) + io.filewrite(utils.millisec2sec(t2s_limited), 't2star map', ref_img) + io.filewrite(s0_limited, 's0 map', ref_img) if verbose: - io.filewrite(utils.millisec2sec(t2s_full), op.join(out_dir, 't2svG.nii'), ref_img) - io.filewrite(s0_full, op.join(out_dir, 's0vG.nii'), ref_img) + io.filewrite(utils.millisec2sec(t2s_full), + 'full t2star map', ref_img + ) + io.filewrite(s0_full, 'full s0 map', ref_img) + # optimally combine data data_oc = combine.make_optcom(catd, tes, masksum, t2s=t2s_full, combmode=combmode) @@ -516,8 +538,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, verbose=verbose, low_mem=low_mem) if verbose: - io.filewrite(utils.unmask(dd, mask), - op.join(out_dir, 'ts_OC_whitened.nii.gz'), ref_img) + io.filewrite(utils.unmask(dd, mask), 'whitened', ref_img) # Perform ICA, calculate metrics, and apply decision tree # Restart when ICA fails to converge or too few BOLD components found @@ -559,9 +580,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, mixing_df = pd.DataFrame(data=mmix, columns=comp_names) mixing_df.to_csv(op.join(out_dir, 'ica_mixing.tsv'), sep='\t', index=False) betas_oc = utils.unmask(computefeats2(data_oc, mmix, mask), mask) - io.filewrite(betas_oc, - op.join(out_dir, 'ica_components.nii.gz'), - ref_img) + io.filewrite(betas_oc, 'ICA components', ref_img) else: LGR.info('Using supplied mixing matrix from ICA') mmix_orig = pd.read_table(op.join(out_dir, 'ica_mixing.tsv')).values @@ -579,9 +598,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, if manacc is not None: comptable = selection.manual_selection(comptable, acc=manacc) betas_oc = utils.unmask(computefeats2(data_oc, mmix, mask), mask) - io.filewrite(betas_oc, - op.join(out_dir, 'ica_components.nii.gz'), - ref_img) + io.filewrite(betas_oc, 'ICA components', ref_img) # Save component table comptable['Description'] = 'ICA fit to dimensionally-reduced optimally combined data.' @@ -623,14 +640,13 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, comptable=comptable, mmix=mmix, n_vols=n_vols, - ref_img=ref_img, - out_dir=out_dir) + ref_img=ref_img) if 'mir' in gscontrol: gsc.minimum_image_regression(data_oc, mmix, mask, comptable, ref_img, out_dir=out_dir) if verbose: - io.writeresults_echoes(catd, mmix, mask, comptable, ref_img, out_dir=out_dir) + io.writeresults_echoes(catd, mmix, mask, comptable, ref_img) if not no_reports: LGR.info('Making figures folder with static component maps and ' From 685a162c72fbbc2f7fc81ccf0e308f90c60b6119 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 23 Feb 2021 16:19:02 -0500 Subject: [PATCH 02/39] Adds .swp to gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 51be73d77..39e49b8a2 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,7 @@ ENV/ # jupyter notebooks .ipynb_checkpoints/ -*.ipynb \ No newline at end of file +*.ipynb + +# vim swap files +*.swp From 83ffe2e19c09ea42a932cf8147c26dc763d34cd0 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 23 Feb 2021 17:53:25 -0500 Subject: [PATCH 03/39] Adds full image functionality --- tedana/decomposition/pca.py | 3 ++- tedana/io.py | 31 +++++++++++++++++++++++++------ tedana/metrics/kundu_fit.py | 29 ++++++++++------------------- tedana/workflows/tedana.py | 2 -- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 8ccd648f3..35c31cf41 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -244,8 +244,9 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, comptable['normalized variance explained'] = varex_norm # write component maps to 4D image + comp_ts_z = stats.zscore(comp_ts, axis=0) comp_maps = utils.unmask(computefeats2(data_oc, comp_ts_z, mask), mask) - io.filewrite(comp_maps, 'PCA components', ref_img) + io.filewrite(comp_maps, 'z-scored PCA components', ref_img) # Select components using decision tree if algorithm == 'kundu': diff --git a/tedana/io.py b/tedana/io.py index b0a5ddb84..24d3bf8de 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -28,8 +28,10 @@ 't2star map': ('t2sv', 'T2starmap'), 's0 map': ('s0v', 'S0map'), 'combined': ('ts_OC', 'desc-optcom_bold'), - 'PCA components': ('pca_components', 'desc-PCA_components'), - 'ICA components': ('betas_OC', 'desc-ICA_components'), + 'z-scored PCA components': ('pca_components', + 'desc-PCA_stat-z_components'), + 'z-scored ICA components': ('betas_OC', + 'desc-ICA_stat-z_components'), 'ICA accepted components': ('betas_hik_OC', 'desc-ICAAccepted_components'), 'z-scored ICA accepted components': ( @@ -44,11 +46,28 @@ 'full t2star map': ('t2svG', 'desc-full_T2starmap'), 'full s0 map': ('s0vG', 'desc-full_S0map'), 'whitened': ('ts_OC_whitened', 'desc-optcomPCAReduced_bold'), + 'echo weight PCA map split': ('e{0}_PCA_comp', + 'echo-{0}_desc-PCA_components'), + 'echo R2 PCA split': ('e{0}_PCA_R2', + 'echo-{0}_desc-PCAR2ModelPredictions_components'), + 'echo S0 PCA split': ('e{0}_PCA_S0', + 'echo-{0}_desc-PCAS0ModelPredictions_components'), + 'PCA component weights': ('pca_weights', + 'desc-PCAAveragingWeights_components'), + 'PCA reduced': ('oc_reduced', 'desc-optcomPCAReduced_bold'), + 'echo weight ICA map split': ('e{0}_ICA_comp', + 'echo-{0}_desc-ICA_components'), + 'echo R2 ICA split': ('e{0}_ICA_R2', + 'echo-{0}_desc-ICAR2ModelPredictions_components'), + 'echo S0 ICA split': ('e{0}_ICA_S0', + 'echo-{0}_desc-ICAS0ModelPredictions_components'), + 'ICA component weights': ('ica_weights', + 'desc-ICAAveragingWeights_components'), 'high kappa ts split': ('hik_ts_e{0}', - 'echo_{0}_desc-Accepted_bold'), + 'echo-{0}_desc-Accepted_bold'), 'low kappa ts split': ('lowk_ts_e{0}', - 'echo_{0}_desc-Rejected_bold'), - 'denoised ts split': ('dn_ts_e{0}', 'echo_{0}_desc-Denoised_bold'), + 'echo-{0}_desc-Rejected_bold'), + 'denoised ts split': ('dn_ts_e{0}', 'echo-{0}_desc-Denoised_bold'), } @@ -305,7 +324,7 @@ def writeresults(ts, mask, comptable, mmix, n_vols, ref_img): write_split_ts(ts, mmix, mask, comptable, ref_img) ts_B = get_coeffs(ts, mmix, mask) - fout = filewrite(ts_B, 'ICA components', ref_img) + fout = filewrite(ts_B, 'z-scored ICA components', ref_img) LGR.info('Writing full ICA coefficient feature set: {}'.format(op.abspath(fout))) if len(acc) != 0: diff --git a/tedana/metrics/kundu_fit.py b/tedana/metrics/kundu_fit.py index b516ccac7..0e1b28e0a 100644 --- a/tedana/metrics/kundu_fit.py +++ b/tedana/metrics/kundu_fit.py @@ -51,11 +51,6 @@ def dependence_metrics(catd, tsoc, mmix, adaptive_mask, tes, ref_img, algorithm : {'kundu_v2', 'kundu_v3', None}, optional Decision tree to be applied to metrics. Determines which maps will be generated and stored in ``metric_maps``. Default: None - label : :obj:`str` or None, optional - Prefix to apply to generated files. Default is None. - out_dir : :obj:`str`, optional - Output directory for generated files. Default is current working - directory. verbose : :obj:`bool`, optional Whether or not to generate additional files. Default is False. @@ -236,9 +231,9 @@ def dependence_metrics(catd, tsoc, mmix, adaptive_mask, tes, ref_img, echo_betas = betas[:, i_echo, :] io.filewrite( utils.unmask(echo_betas, mask), - op.join(out_dir, 'echo-{0}_desc-{1}_components.nii.gz'.format( - i_echo + 1, label)), - ref_img + 'echo weight ICA map', + ref_img, + echo=(i_echo + 1) ) # Echo-specific maps of predicted values for R2 and S0 models for each @@ -246,26 +241,22 @@ def dependence_metrics(catd, tsoc, mmix, adaptive_mask, tes, ref_img, echo_pred_R2_maps = pred_R2_maps[:, i_echo, :] io.filewrite( utils.unmask(echo_pred_R2_maps, mask), - op.join(out_dir, 'echo-{0}_desc-{1}R2ModelPredictions_components.nii.gz'.format( - i_echo + 1, - label - )), - ref_img + 'echo R2 ICA', + ref_img, + echo=(i_echo + 1) ) echo_pred_S0_maps = pred_S0_maps[:, i_echo, :] io.filewrite( utils.unmask(echo_pred_S0_maps, mask), - op.join(out_dir, 'echo-{0}_desc-{1}S0ModelPredictions_components.nii.gz'.format( - i_echo + 1, - label - )), - ref_img + 'echo S0 ICA', + ref_img, + echo=(i_echo + 1) ) # Weight maps used to average metrics across voxels io.filewrite( utils.unmask(Z_maps ** 2., mask), - op.join(out_dir, 'desc-{0}AveragingWeights_components.nii.gz'.format(label)), + 'ICA component weights', ref_img ) del pred_R2_maps, pred_S0_maps diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index fd4bc81b9..2ccbd888f 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -585,8 +585,6 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, keep_restarting = False else: keep_restarting = False - - io.filewrite(betas_oc, 'ICA components', ref_img) else: LGR.info('Using supplied mixing matrix from ICA') mmix_orig = pd.read_table(op.join(out_dir, 'desc-ICA_mixing.tsv')).values From a82f6f6e38954dcb101d9177aa107c050f2455bf Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 23 Feb 2021 19:09:16 -0500 Subject: [PATCH 04/39] Revert z-scoring fix --- tedana/decomposition/pca.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 35c31cf41..68b9d4512 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -244,8 +244,7 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, comptable['normalized variance explained'] = varex_norm # write component maps to 4D image - comp_ts_z = stats.zscore(comp_ts, axis=0) - comp_maps = utils.unmask(computefeats2(data_oc, comp_ts_z, mask), mask) + comp_maps = utils.unmask(computefeats2(data_oc, comp_ts, mask), mask) io.filewrite(comp_maps, 'z-scored PCA components', ref_img) # Select components using decision tree From 8d08f71f758c2d1d15380a0916d264f98cec1a6a Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Wed, 24 Feb 2021 15:36:15 -0500 Subject: [PATCH 05/39] Fixes ICA component miss --- tedana/gscontrol.py | 16 +++++++++------- tedana/io.py | 18 ++++++++++++++---- tedana/metrics/kundu_fit.py | 8 ++++---- tedana/workflows/tedana.py | 4 ++-- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/tedana/gscontrol.py b/tedana/gscontrol.py index b26b389f0..a880cb3f7 100644 --- a/tedana/gscontrol.py +++ b/tedana/gscontrol.py @@ -81,7 +81,7 @@ def gscontrol_raw(catd, optcom, n_echos, ref_img, out_dir='.', dtrank=4): sphis -= sphis.mean() io.filewrite( utils.unmask(sphis, Gmask), - op.join(out_dir, 'desc-globalSignal_map.nii.gz'), + 'gs map', ref_img ) @@ -102,13 +102,13 @@ def gscontrol_raw(catd, optcom, n_echos, ref_img, out_dir='.', dtrank=4): io.filewrite( optcom, - op.join(out_dir, 'desc-optcomWithGlobalSignal_bold.nii.gz'), + 'has gs combined', ref_img ) dm_optcom = utils.unmask(tsoc_nogs, Gmask) io.filewrite( dm_optcom, - op.join(out_dir, 'desc-optcomNoGlobalSignal_bold.nii.gz'), + 'removed gs combined', ref_img ) @@ -219,7 +219,9 @@ def minimum_image_regression(optcom_ts, mmix, mask, comptable, ref_img, out_dir= t1_map = mehk_ts.min(axis=-1) # map of T1-like effect t1_map -= t1_map.mean() io.filewrite( - utils.unmask(t1_map, mask), op.join(out_dir, "desc-T1likeEffect_min.nii.gz"), ref_img + utils.unmask(t1_map, mask), + 't1 like', + ref_img ) t1_map = t1_map[:, np.newaxis] @@ -233,7 +235,7 @@ def minimum_image_regression(optcom_ts, mmix, mask, comptable, ref_img, out_dir= hik_ts = mehk_noT1gs * optcom_std # rescale io.filewrite( utils.unmask(hik_ts, mask), - op.join(out_dir, "desc-optcomAcceptedMIRDenoised_bold.nii.gz"), + 'ICA accepted mir denoised', ref_img, ) @@ -241,7 +243,7 @@ def minimum_image_regression(optcom_ts, mmix, mask, comptable, ref_img, out_dir= medn_ts = optcom_mean + ((mehk_noT1gs + resid) * optcom_std) io.filewrite( utils.unmask(medn_ts, mask), - op.join(out_dir, "desc-optcomMIRDenoised_bold.nii.gz"), + 'mir denoised', ref_img, ) @@ -258,7 +260,7 @@ def minimum_image_regression(optcom_ts, mmix, mask, comptable, ref_img, out_dir= comp_pes_norm = np.linalg.lstsq(mmix_noT1gs_z.T, optcom_z.T, rcond=None)[0].T io.filewrite( utils.unmask(comp_pes_norm[:, 2:], mask), - op.join(out_dir, "desc-ICAAcceptedMIRDenoised_components.nii.gz"), + 'ICA accepted mir component weights', ref_img, ) mixing_df = pd.DataFrame(data=mmix_noT1gs.T, columns=comptable["Component"].values) diff --git a/tedana/io.py b/tedana/io.py index 24d3bf8de..ebfd640bf 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -20,7 +20,7 @@ outdir = '.' prefix = '' -convention = 'kundu' +convention = 'bids' img_table = { @@ -28,6 +28,7 @@ 't2star map': ('t2sv', 'T2starmap'), 's0 map': ('s0v', 'S0map'), 'combined': ('ts_OC', 'desc-optcom_bold'), + 'ICA components': ('ica_components', 'desc-ICA_components'), 'z-scored PCA components': ('pca_components', 'desc-PCA_stat-z_components'), 'z-scored ICA components': ('betas_OC', @@ -37,8 +38,6 @@ 'z-scored ICA accepted components': ( 'feats_OC2', 'desc-ICAAccepted_stat-z_components'), - 'z-scored ICA components': ('ica_components', - 'desc-ICA_stat-z_components'), 'denoised ts': ('dn_ts_OC', 'desc-optcomDenoised_bold'), 'high kappa ts': ('hik_ts_OC', 'desc-optcomAccepted_bold'), 'low kappa ts': ('lowk_ts_OC', 'desc-optcomRejected_bold'), @@ -68,6 +67,17 @@ 'low kappa ts split': ('lowk_ts_e{0}', 'echo-{0}_desc-Rejected_bold'), 'denoised ts split': ('dn_ts_e{0}', 'echo-{0}_desc-Denoised_bold'), + # global signal outputs + 'gs map': ('T1gs', 'desc-globalSignal_map'), + 'has gs combined': ('tsoc_orig', 'desc-optcomWithGlobalSignal_bold'), + 'removed gs combined': ('tsoc_nogs', + 'desc-optcomNoGlobalSingal_bold'), + 't1 like': ('sphis_hik', 'desc-T1likeEffect_min'), + 'ICA accepted mir denoised': ('hik_ts_OC_MIR', + 'desc-optcomAcceptedMIRDenoised_bold'), + 'mir denoised': ('dn_ts_OC_MIR', 'desc-optcomMIRDenoised_bold'), + 'ICA accepted mir component weights': ('betas_hik_OC_MIR', + 'desc-ICAAcceptedMIRDenoised_components'), } @@ -324,7 +334,7 @@ def writeresults(ts, mask, comptable, mmix, n_vols, ref_img): write_split_ts(ts, mmix, mask, comptable, ref_img) ts_B = get_coeffs(ts, mmix, mask) - fout = filewrite(ts_B, 'z-scored ICA components', ref_img) + fout = filewrite(ts_B, 'ICA components', ref_img) LGR.info('Writing full ICA coefficient feature set: {}'.format(op.abspath(fout))) if len(acc) != 0: diff --git a/tedana/metrics/kundu_fit.py b/tedana/metrics/kundu_fit.py index 0e1b28e0a..d717ba743 100644 --- a/tedana/metrics/kundu_fit.py +++ b/tedana/metrics/kundu_fit.py @@ -231,7 +231,7 @@ def dependence_metrics(catd, tsoc, mmix, adaptive_mask, tes, ref_img, echo_betas = betas[:, i_echo, :] io.filewrite( utils.unmask(echo_betas, mask), - 'echo weight ICA map', + 'echo weight ' + label + ' map', ref_img, echo=(i_echo + 1) ) @@ -241,14 +241,14 @@ def dependence_metrics(catd, tsoc, mmix, adaptive_mask, tes, ref_img, echo_pred_R2_maps = pred_R2_maps[:, i_echo, :] io.filewrite( utils.unmask(echo_pred_R2_maps, mask), - 'echo R2 ICA', + 'echo R2 ' + label, ref_img, echo=(i_echo + 1) ) echo_pred_S0_maps = pred_S0_maps[:, i_echo, :] io.filewrite( utils.unmask(echo_pred_S0_maps, mask), - 'echo S0 ICA', + 'echo S0 ' + label, ref_img, echo=(i_echo + 1) ) @@ -256,7 +256,7 @@ def dependence_metrics(catd, tsoc, mmix, adaptive_mask, tes, ref_img, # Weight maps used to average metrics across voxels io.filewrite( utils.unmask(Z_maps ** 2., mask), - 'ICA component weights', + label + ' component weights', ref_img ) del pred_R2_maps, pred_S0_maps diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 2ccbd888f..e37f880bc 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -89,7 +89,7 @@ def _get_parser(): choices=['kundu', 'bids'], help=("Filenaming convention. bids will use " "the latest BIDS derivatives version."), - default='kundu') + default='bids') optional.add_argument('--fittype', dest='fittype', action='store', @@ -248,7 +248,7 @@ def _get_parser(): def tedana_workflow(data, tes, out_dir='.', mask=None, - convention='kundu', prefix='', + convention='bids', prefix='', fittype='loglin', combmode='t2s', tedpca='mdl', fixed_seed=42, maxit=500, maxrestart=10, tedort=False, gscontrol=None, From 1c395ef65a17de2c5af1e7a2d67c3c42eb7f3b4a Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Wed, 24 Feb 2021 16:40:25 -0500 Subject: [PATCH 06/39] Fix style --- tedana/io.py | 99 +++++++++++++++++++++---------------- tedana/metrics/kundu_fit.py | 1 - tedana/tests/test_io.py | 24 ++++----- tedana/workflows/t2smap.py | 1 + tedana/workflows/tedana.py | 8 +-- 5 files changed, 75 insertions(+), 58 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index ebfd640bf..409a8d974 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -3,11 +3,9 @@ """ import logging import os.path as op -from enum import Enum, unique import numpy as np import nibabel as nib -from nibabel.filename_parser import splitext_addext from nilearn._utils import check_niimg from nilearn.image import new_img_like @@ -29,15 +27,18 @@ 's0 map': ('s0v', 'S0map'), 'combined': ('ts_OC', 'desc-optcom_bold'), 'ICA components': ('ica_components', 'desc-ICA_components'), - 'z-scored PCA components': ('pca_components', - 'desc-PCA_stat-z_components'), - 'z-scored ICA components': ('betas_OC', - 'desc-ICA_stat-z_components'), - 'ICA accepted components': ('betas_hik_OC', - 'desc-ICAAccepted_components'), + 'z-scored PCA components': ( + 'pca_components', 'desc-PCA_stat-z_components' + ), + 'z-scored ICA components': ( + 'betas_OC', 'desc-ICA_stat-z_components' + ), + 'ICA accepted components': ( + 'betas_hik_OC', 'desc-ICAAccepted_components' + ), 'z-scored ICA accepted components': ( - 'feats_OC2', - 'desc-ICAAccepted_stat-z_components'), + 'feats_OC2', 'desc-ICAAccepted_stat-z_components' + ), 'denoised ts': ('dn_ts_OC', 'desc-optcomDenoised_bold'), 'high kappa ts': ('hik_ts_OC', 'desc-optcomAccepted_bold'), 'low kappa ts': ('lowk_ts_OC', 'desc-optcomRejected_bold'), @@ -45,39 +46,52 @@ 'full t2star map': ('t2svG', 'desc-full_T2starmap'), 'full s0 map': ('s0vG', 'desc-full_S0map'), 'whitened': ('ts_OC_whitened', 'desc-optcomPCAReduced_bold'), - 'echo weight PCA map split': ('e{0}_PCA_comp', - 'echo-{0}_desc-PCA_components'), - 'echo R2 PCA split': ('e{0}_PCA_R2', - 'echo-{0}_desc-PCAR2ModelPredictions_components'), - 'echo S0 PCA split': ('e{0}_PCA_S0', - 'echo-{0}_desc-PCAS0ModelPredictions_components'), - 'PCA component weights': ('pca_weights', - 'desc-PCAAveragingWeights_components'), + 'echo weight PCA map split': ( + 'e{0}_PCA_comp', 'echo-{0}_desc-PCA_components' + ), + 'echo R2 PCA split': ( + 'e{0}_PCA_R2', 'echo-{0}_desc-PCAR2ModelPredictions_components' + ), + 'echo S0 PCA split': ( + 'e{0}_PCA_S0', 'echo-{0}_desc-PCAS0ModelPredictions_components' + ), + 'PCA component weights': ( + 'pca_weights', 'desc-PCAAveragingWeights_components' + ), 'PCA reduced': ('oc_reduced', 'desc-optcomPCAReduced_bold'), - 'echo weight ICA map split': ('e{0}_ICA_comp', - 'echo-{0}_desc-ICA_components'), - 'echo R2 ICA split': ('e{0}_ICA_R2', - 'echo-{0}_desc-ICAR2ModelPredictions_components'), - 'echo S0 ICA split': ('e{0}_ICA_S0', - 'echo-{0}_desc-ICAS0ModelPredictions_components'), - 'ICA component weights': ('ica_weights', - 'desc-ICAAveragingWeights_components'), - 'high kappa ts split': ('hik_ts_e{0}', - 'echo-{0}_desc-Accepted_bold'), - 'low kappa ts split': ('lowk_ts_e{0}', - 'echo-{0}_desc-Rejected_bold'), + 'echo weight ICA map split': ( + 'e{0}_ICA_comp', 'echo-{0}_desc-ICA_components' + ), + 'echo R2 ICA split': ( + 'e{0}_ICA_R2', 'echo-{0}_desc-ICAR2ModelPredictions_components' + ), + 'echo S0 ICA split': ( + 'e{0}_ICA_S0', 'echo-{0}_desc-ICAS0ModelPredictions_components' + ), + 'ICA component weights': ( + 'ica_weights', 'desc-ICAAveragingWeights_components' + ), + 'high kappa ts split': ( + 'hik_ts_e{0}', 'echo-{0}_desc-Accepted_bold' + ), + 'low kappa ts split': ( + 'lowk_ts_e{0}', 'echo-{0}_desc-Rejected_bold' + ), 'denoised ts split': ('dn_ts_e{0}', 'echo-{0}_desc-Denoised_bold'), # global signal outputs 'gs map': ('T1gs', 'desc-globalSignal_map'), 'has gs combined': ('tsoc_orig', 'desc-optcomWithGlobalSignal_bold'), - 'removed gs combined': ('tsoc_nogs', - 'desc-optcomNoGlobalSingal_bold'), + 'removed gs combined': ( + 'tsoc_nogs', 'desc-optcomNoGlobalSignal_bold' + ), 't1 like': ('sphis_hik', 'desc-T1likeEffect_min'), - 'ICA accepted mir denoised': ('hik_ts_OC_MIR', - 'desc-optcomAcceptedMIRDenoised_bold'), + 'ICA accepted mir denoised': ( + 'hik_ts_OC_MIR', 'desc-optcomAcceptedMIRDenoised_bold' + ), 'mir denoised': ('dn_ts_OC_MIR', 'desc-optcomMIRDenoised_bold'), - 'ICA accepted mir component weights': ('betas_hik_OC_MIR', - 'desc-ICAAcceptedMIRDenoised_components'), + 'ICA accepted mir component weights': ( + 'betas_hik_OC_MIR', 'desc-ICAAcceptedMIRDenoised_components' + ), } @@ -91,7 +105,7 @@ def gen_img_name(img_type, echo=0): The description of the image. Must be a key in io.img_table echo : :obj: `int` The echo number of the image. - + Returns ------- The full path for the image name @@ -115,7 +129,7 @@ def gen_img_name(img_type, echo=0): if convention: raise RuntimeError('Naming convention %s not supported' % convention - ) + ) else: raise RuntimeError('No naming convention given!') if echo: @@ -128,7 +142,7 @@ def gen_img_name(img_type, echo=0): else: basename = format_string return op.join(outdir, prefix + basename) - + def split_ts(data, mmix, mask, comptable): """ @@ -387,8 +401,9 @@ def writeresults_echoes(catd, mmix, mask, comptable, ref_img): for i_echo in range(catd.shape[1]): LGR.info('Writing Kappa-filtered echo #{:01d} timeseries'.format(i_echo + 1)) - write_split_ts(catd[:, i_echo, :], mmix, mask, comptable, - ref_img, echo=(i_echo + 1) + write_split_ts( + catd[:, i_echo, :], mmix, mask, comptable, ref_img, + echo=(i_echo + 1) ) @@ -429,7 +444,7 @@ def new_nii_like(ref_img, data, affine=None, copy_header=True): def filewrite(data, img_type, ref_img, gzip=True, copy_header=True, - echo=0): + echo=0): """ Writes `data` to `filename` in format of `ref_img` diff --git a/tedana/metrics/kundu_fit.py b/tedana/metrics/kundu_fit.py index d717ba743..8adedde6b 100644 --- a/tedana/metrics/kundu_fit.py +++ b/tedana/metrics/kundu_fit.py @@ -2,7 +2,6 @@ Fit models. """ import logging -import os.path as op import numpy as np import pandas as pd diff --git a/tedana/tests/test_io.py b/tedana/tests/test_io.py index b19d5ab04..f6329ad4f 100644 --- a/tedana/tests/test_io.py +++ b/tedana/tests/test_io.py @@ -138,22 +138,24 @@ def test_smoke_writefeats(): def test_smoke_filewrite(): """ - Ensures that filewrite writes out a neuroimage with random input, - since there is no name, remove the image named .nii + Ensures that filewrite fails for no known image type, write a known key + in both bids and kundu formats """ - n_samples, n_times, _ = 64350, 10, 6 + n_samples, _, _ = 64350, 10, 6 data_1d = np.random.random((n_samples)) - data_2d = np.random.random((n_samples, n_times)) - filename = "" ref_img = os.path.join(data_dir, 'mask.nii.gz') - assert me.filewrite(data_1d, filename, ref_img) is not None - assert me.filewrite(data_2d, filename, ref_img) is not None + with pytest.raises(KeyError): + me.filewrite(data_1d, '', ref_img) - try: - os.remove(".nii.gz") - except OSError: - print(".nii not generated") + for convention in ('bids', 'kundu'): + me.convention = convention + fname = me.filewrite(data_1d, 't2star map', ref_img) + assert fname is not None + try: + os.remove(fname) + except OSError: + print('File not generated!') def test_smoke_load_data(): diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index 81ada0dac..b3a38d460 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -178,6 +178,7 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, out_dir = op.abspath(out_dir) if not op.isdir(out_dir): os.mkdir(out_dir) + io.outdir = out_dir if debug and not quiet: logging.basicConfig(level=logging.DEBUG) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index e37f880bc..9a0d355c1 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -511,13 +511,13 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, LGR.debug('Setting cap on T2* map at {:.5f}s'.format( utils.millisec2sec(cap_t2s))) t2s_limited[t2s_limited > cap_t2s * 10] = cap_t2s - io.filewrite(utils.millisec2sec(t2s_limited), 't2star map', ref_img) + io.filewrite(utils.millisec2sec(t2s_limited), + 't2star map', ref_img) io.filewrite(s0_limited, 's0 map', ref_img) if verbose: - io.filewrite(utils.millisec2sec(t2s_full), - 'full t2star map', ref_img - ) + io.filewrite(utils.millisec2sec(t2s_full), + 'full t2star map', ref_img) io.filewrite(s0_full, 'full s0 map', ref_img) # optimally combine data From 3ea7ba465077eb61b1af27f5954922ecfaa92eb1 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 12 Mar 2021 13:18:18 -0500 Subject: [PATCH 07/39] Adds --prefix, --convention to t2map.py --- tedana/workflows/t2smap.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index b3a38d460..afa1c6919 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -64,6 +64,18 @@ def _get_parser(): 'Dependent ANAlysis. Must be in the same ' 'space as `data`.'), default=None) + optional.add_argument('--prefix', + dest='prefix', + type=str, + help='Prefix for filenames generated.', + default='') + optional.add_argument('--convention', + dest='convention', + action='store', + choices=['kundu', 'bids'], + help=('Filenaming convention. bids will use ' + 'the latest BIDS derivatives version.'), + default='bids') optional.add_argument('--fittype', dest='fittype', action='store', @@ -117,6 +129,7 @@ def _get_parser(): def t2smap_workflow(data, tes, out_dir='.', mask=None, + prefix='', convention='bids', fittype='loglin', fitmode='all', combmode='t2s', debug=False, quiet=False): """ @@ -179,6 +192,8 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, if not op.isdir(out_dir): os.mkdir(out_dir) io.outdir = out_dir + io.prefix = prefix + io.convention = convention if debug and not quiet: logging.basicConfig(level=logging.DEBUG) From daf16ce893ce582143233d715f339a3dd7f6677a Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 12 Mar 2021 14:06:45 -0500 Subject: [PATCH 08/39] Fix io tests --- tedana/tests/test_io.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/tedana/tests/test_io.py b/tedana/tests/test_io.py index f6329ad4f..3875b261b 100644 --- a/tedana/tests/test_io.py +++ b/tedana/tests/test_io.py @@ -106,13 +106,12 @@ def test_smoke_write_split_ts(): assert me.write_split_ts(data, mmix, mask, comptable, ref_img) is not None # TODO: midk_ts.nii is never generated? - for filename in ["hik_ts_.nii.gz", "lowk_ts_.nii.gz", "dn_ts_.nii.gz"]: + fn = me.gen_img_name + split = ('high kappa ts', 'low kappa ts', 'denoised ts') + fnames = [fn(f) + '.nii.gz' for f in split] + for filename in fnames: # remove all files generated - try: - os.remove(filename) - except OSError: - print(filename + " not generated") - pass + os.remove(filename) def test_smoke_writefeats(): @@ -129,11 +128,10 @@ def test_smoke_writefeats(): assert me.writefeats(data, mmix, mask, ref_img) is not None # this only generates feats_.nii, so delete that - try: - os.remove("feats_.nii.gz") - except OSError: - print("feats_.nii not generated") - pass + os.remove( + me.gen_img_name('z-scored ICA accepted components') + + '.nii.gz' + ) def test_smoke_filewrite(): From fe67e022dda38cbc23fb0b548757ae65c6eb1a7d Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 12 Mar 2021 16:25:45 -0500 Subject: [PATCH 09/39] Adds clarifying comment --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index 409a8d974..70bf2107a 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -18,7 +18,7 @@ outdir = '.' prefix = '' -convention = 'bids' +convention = 'bids' # overridden in API or CLI calls img_table = { From 2403af805eefbe0754accfdd073cc90b7ebdd8a6 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 15 Mar 2021 10:40:56 -0400 Subject: [PATCH 10/39] Nest dict, move to new file constants.py --- tedana/constants.py | 147 ++++++++++++++++++++++++++++++++++++++++ tedana/io.py | 99 +++------------------------ tedana/tests/test_io.py | 2 +- 3 files changed, 159 insertions(+), 89 deletions(-) create mode 100644 tedana/constants.py diff --git a/tedana/constants.py b/tedana/constants.py new file mode 100644 index 000000000..2a7dfcbbe --- /dev/null +++ b/tedana/constants.py @@ -0,0 +1,147 @@ +""" +Constants for tedana +""" + +allowed_conventions = ('orig', 'bidsv1.5.0', 'bids') + +# filename tables +img_table = { + 'adaptive mask': { + 'orig': 'adaptive_mask', + 'bidsv1.5.0': 'desc-adaptiveGoodSignal_mask', + }, + 't2star map': { + 'orig': 't2sv', + 'bidsv1.5.0': 'T2starmap', + }, + 's0 map': { + 'orig': 's0v', + 'bidsv1.5.0': 'S0map', + }, + 'combined': { + 'orig': 'ts_OC', + 'bidsv1.5.0': 'desc-optcom_bold', + }, + 'ICA components': { + 'orig': 'ica_components', + 'bidsv1.5.0': 'desc-ICA_components', + }, + 'z-scored PCA components': { + 'orig': 'pca_components', + 'bidsv1.5.0': 'desc-PCA_stat-z_components', + }, + 'z-scored ICA components': { + 'orig': 'betas_OC', + 'bidsv1.5.0': 'desc-ICA_stat-z_components', + }, + 'ICA accepted components': { + 'orig': 'betas_hik_OC', + 'bidsv1.5.0': 'desc-ICAAccepted_components', + }, + 'z-scored ICA accepted components': { + 'orig': 'feats_OC2', + 'bidsv1.5.0': 'desc-ICAAccepted_stat-z_components', + }, + 'denoised ts': { + 'orig': 'dn_ts_OC', + 'bidsv1.5.0': 'desc-optcomDenoised_bold', + }, + 'high kappa ts': { + 'orig': 'hik_ts_OC', + 'bidsv1.5.0': 'desc-optcomAccepted_bold', + }, + 'low kappa ts': { + 'orig': 'lowk_ts_OC', + 'bidsv1.5.0': 'desc-optcomRejected_bold', + }, + # verbose outputs + 'full t2star map': { + 'orig': 't2svG', + 'bidsv1.5.0': 'desc-full_T2starmap', + }, + 'full s0 map': { + 'orig': 's0vG', + 'bidsv1.5.0': 'desc-full_S0map', + }, + 'whitened': { + 'orig': 'ts_OC_whitened', + 'bidsv1.5.0': 'desc-optcomPCAReduced_bold', + }, + 'echo weight PCA map split': { + 'orig': 'e{0}_PCA_comp', + 'bidsv1.5.0': 'echo-{0}_desc-PCA_components', + }, + 'echo R2 PCA split': { + 'orig': 'e{0}_PCA_R2', + 'bidsv1.5.0': 'echo-{0}_desc-PCAR2ModelPredictions_components', + }, + 'echo S0 PCA split': { + 'orig': 'e{0}_PCA_S0', + 'bidsv1.5.0': 'echo-{0}_desc-PCAS0ModelPredictions_components', + }, + 'PCA component weights': { + 'orig': 'pca_weights', + 'bidsv1.5.0': 'desc-PCAAveragingWeights_components', + }, + 'PCA reduced': { + 'orig': 'oc_reduced', + 'bidsv1.5.0': 'desc-optcomPCAReduced_bold', + }, + 'echo weight ICA map split': { + 'orig': 'e{0}_ICA_comp', + 'bidsv1.5.0': 'echo-{0}_desc-ICA_components', + }, + 'echo R2 ICA split': { + 'orig': 'e{0}_ICA_R2', + 'bidsv1.5.0': 'echo-{0}_desc-ICAR2ModelPredictions_components', + }, + 'echo S0 ICA split': { + 'orig': 'e{0}_ICA_S0', + 'bidsv1.5.0': 'echo-{0}_desc-ICAS0ModelPredictions_components', + }, + 'ICA component weights': { + 'orig': 'ica_weights', + 'bidsv1.5.0': 'desc-ICAAveragingWeights_components', + }, + 'high kappa ts split': { + 'orig': 'hik_ts_e{0}', + 'bidsv1.5.0': 'echo-{0}_desc-Accepted_bold', + }, + 'low kappa ts split': { + 'orig': 'lowk_ts_e{0}', + 'bidsv1.5.0': 'echo-{0}_desc-Rejected_bold', + }, + 'denoised ts split': { + 'orig': 'dn_ts_e{0}', + 'bidsv1.5.0': 'echo-{0}_desc-Denoised_bold', + }, + # global signal outputs + 'gs map': { + 'orig': 'T1gs', + 'bidsv1.5.0': 'desc-globalSignal_map', + }, + 'has gs combined': { + 'orig': 'tsoc_orig', + 'bidsv1.5.0': 'desc-optcomWithGlobalSignal_bold', + }, + 'removed gs combined': { + 'orig': 'tsoc_nogs', + 'bidsv1.5.0': 'desc-optcomNoGlobalSignal_bold', + }, + 't1 like': { + 'orig': 'sphis_hik', + 'bidsv1.5.0': 'desc-T1likeEffect_min', + }, + 'ICA accepted mir denoised': { + 'orig': 'hik_ts_OC_MIR', + 'bidsv1.5.0': 'desc-optcomAcceptedMIRDenoised_bold', + }, + 'mir denoised': { + 'orig': 'dn_ts_OC_MIR', + 'bidsv1.5.0': 'desc-optcomMIRDenoised_bold', + }, + 'ICA accepted mir component weights': { + 'orig': 'betas_hik_OC_MIR', + 'bidsv1.5.0': 'desc-ICAAcceptedMIRDenoised_components', + }, +} diff --git a/tedana/io.py b/tedana/io.py index 70bf2107a..53ad0926a 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -11,6 +11,8 @@ from tedana import utils from tedana.stats import computefeats2, get_coeffs +from .constants import allowed_conventions, img_table + LGR = logging.getLogger(__name__) RepLGR = logging.getLogger('REPORT') @@ -18,81 +20,7 @@ outdir = '.' prefix = '' -convention = 'bids' # overridden in API or CLI calls - - -img_table = { - 'adaptive mask': ('adaptive_mask', 'desc-adaptiveGoodSignal_mask'), - 't2star map': ('t2sv', 'T2starmap'), - 's0 map': ('s0v', 'S0map'), - 'combined': ('ts_OC', 'desc-optcom_bold'), - 'ICA components': ('ica_components', 'desc-ICA_components'), - 'z-scored PCA components': ( - 'pca_components', 'desc-PCA_stat-z_components' - ), - 'z-scored ICA components': ( - 'betas_OC', 'desc-ICA_stat-z_components' - ), - 'ICA accepted components': ( - 'betas_hik_OC', 'desc-ICAAccepted_components' - ), - 'z-scored ICA accepted components': ( - 'feats_OC2', 'desc-ICAAccepted_stat-z_components' - ), - 'denoised ts': ('dn_ts_OC', 'desc-optcomDenoised_bold'), - 'high kappa ts': ('hik_ts_OC', 'desc-optcomAccepted_bold'), - 'low kappa ts': ('lowk_ts_OC', 'desc-optcomRejected_bold'), - # verbose outputs - 'full t2star map': ('t2svG', 'desc-full_T2starmap'), - 'full s0 map': ('s0vG', 'desc-full_S0map'), - 'whitened': ('ts_OC_whitened', 'desc-optcomPCAReduced_bold'), - 'echo weight PCA map split': ( - 'e{0}_PCA_comp', 'echo-{0}_desc-PCA_components' - ), - 'echo R2 PCA split': ( - 'e{0}_PCA_R2', 'echo-{0}_desc-PCAR2ModelPredictions_components' - ), - 'echo S0 PCA split': ( - 'e{0}_PCA_S0', 'echo-{0}_desc-PCAS0ModelPredictions_components' - ), - 'PCA component weights': ( - 'pca_weights', 'desc-PCAAveragingWeights_components' - ), - 'PCA reduced': ('oc_reduced', 'desc-optcomPCAReduced_bold'), - 'echo weight ICA map split': ( - 'e{0}_ICA_comp', 'echo-{0}_desc-ICA_components' - ), - 'echo R2 ICA split': ( - 'e{0}_ICA_R2', 'echo-{0}_desc-ICAR2ModelPredictions_components' - ), - 'echo S0 ICA split': ( - 'e{0}_ICA_S0', 'echo-{0}_desc-ICAS0ModelPredictions_components' - ), - 'ICA component weights': ( - 'ica_weights', 'desc-ICAAveragingWeights_components' - ), - 'high kappa ts split': ( - 'hik_ts_e{0}', 'echo-{0}_desc-Accepted_bold' - ), - 'low kappa ts split': ( - 'lowk_ts_e{0}', 'echo-{0}_desc-Rejected_bold' - ), - 'denoised ts split': ('dn_ts_e{0}', 'echo-{0}_desc-Denoised_bold'), - # global signal outputs - 'gs map': ('T1gs', 'desc-globalSignal_map'), - 'has gs combined': ('tsoc_orig', 'desc-optcomWithGlobalSignal_bold'), - 'removed gs combined': ( - 'tsoc_nogs', 'desc-optcomNoGlobalSignal_bold' - ), - 't1 like': ('sphis_hik', 'desc-T1likeEffect_min'), - 'ICA accepted mir denoised': ( - 'hik_ts_OC_MIR', 'desc-optcomAcceptedMIRDenoised_bold' - ), - 'mir denoised': ('dn_ts_OC_MIR', 'desc-optcomMIRDenoised_bold'), - 'ICA accepted mir component weights': ( - 'betas_hik_OC_MIR', 'desc-ICAAcceptedMIRDenoised_components' - ), -} +convention = 'bids' # overridden in API or CLI calls def gen_img_name(img_type, echo=0): @@ -120,21 +48,16 @@ def gen_img_name(img_type, echo=0): -------- io.img_table, a dict for translating various naming types """ - - if convention == 'kundu': - tuple_idx = 0 - elif convention == 'bids': - tuple_idx = 1 - else: - if convention: - raise RuntimeError('Naming convention %s not supported' % - convention - ) - else: - raise RuntimeError('No naming convention given!') + if not convention: + raise RuntimeError('Convention not set') + key = convention + if key == 'bids': + key = 'bidsv1.5.0' + elif key not in allowed_conventions: + raise RuntimeError('Convention %s not allowed' % convention) if echo: img_type += ' split' - format_string = img_table[img_type][tuple_idx] + format_string = img_table[img_type][key] if echo and not ('{' in format_string): raise ValueError('Echo supplied when not supported!') elif echo: diff --git a/tedana/tests/test_io.py b/tedana/tests/test_io.py index 3875b261b..9b69a1044 100644 --- a/tedana/tests/test_io.py +++ b/tedana/tests/test_io.py @@ -146,7 +146,7 @@ def test_smoke_filewrite(): with pytest.raises(KeyError): me.filewrite(data_1d, '', ref_img) - for convention in ('bids', 'kundu'): + for convention in ('bids', 'orig'): me.convention = convention fname = me.filewrite(data_1d, 't2star map', ref_img) assert fname is not None From fad0845059b4506019139ad682544cca187f2903 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 15 Mar 2021 11:32:00 -0400 Subject: [PATCH 11/39] Switches bids pointer method --- tedana/constants.py | 4 +++- tedana/io.py | 8 +++----- tedana/tests/test_io.py | 5 +++-- tedana/workflows/t2smap.py | 4 +++- tedana/workflows/tedana.py | 4 +++- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tedana/constants.py b/tedana/constants.py index 2a7dfcbbe..851ea4f53 100644 --- a/tedana/constants.py +++ b/tedana/constants.py @@ -2,7 +2,9 @@ Constants for tedana """ -allowed_conventions = ('orig', 'bidsv1.5.0', 'bids') +allowed_conventions = ('orig', 'bidsv1.5.0') + +bids = 'bidsv1.5.0' # filename tables img_table = { diff --git a/tedana/io.py b/tedana/io.py index 53ad0926a..82633db97 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -11,7 +11,7 @@ from tedana import utils from tedana.stats import computefeats2, get_coeffs -from .constants import allowed_conventions, img_table +from .constants import allowed_conventions, img_table, bids LGR = logging.getLogger(__name__) @@ -20,7 +20,7 @@ outdir = '.' prefix = '' -convention = 'bids' # overridden in API or CLI calls +convention = bids # overridden in API or CLI calls def gen_img_name(img_type, echo=0): @@ -51,9 +51,7 @@ def gen_img_name(img_type, echo=0): if not convention: raise RuntimeError('Convention not set') key = convention - if key == 'bids': - key = 'bidsv1.5.0' - elif key not in allowed_conventions: + if key not in allowed_conventions: raise RuntimeError('Convention %s not allowed' % convention) if echo: img_type += ' split' diff --git a/tedana/tests/test_io.py b/tedana/tests/test_io.py index 9b69a1044..df873fcbf 100644 --- a/tedana/tests/test_io.py +++ b/tedana/tests/test_io.py @@ -8,6 +8,7 @@ import pandas as pd from tedana import io as me +from tedana import constants from tedana.tests.test_utils import fnames, tes from tedana.tests.utils import get_test_data_path @@ -137,7 +138,7 @@ def test_smoke_writefeats(): def test_smoke_filewrite(): """ Ensures that filewrite fails for no known image type, write a known key - in both bids and kundu formats + in both bids and orig formats """ n_samples, _, _ = 64350, 10, 6 data_1d = np.random.random((n_samples)) @@ -146,7 +147,7 @@ def test_smoke_filewrite(): with pytest.raises(KeyError): me.filewrite(data_1d, '', ref_img) - for convention in ('bids', 'orig'): + for convention in (constants.bids, 'orig'): me.convention = convention fname = me.filewrite(data_1d, 't2star map', ref_img) assert fname is not None diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index afa1c6919..12edcab4b 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -11,7 +11,7 @@ from scipy import stats from threadpoolctl import threadpool_limits -from tedana import (combine, decay, io, utils, __version__) +from tedana import (combine, decay, io, utils, constants, __version__) from tedana.workflows.parser_utils import is_valid_file LGR = logging.getLogger(__name__) @@ -193,6 +193,8 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, os.mkdir(out_dir) io.outdir = out_dir io.prefix = prefix + if convention == 'bids': + convention = constants.bids io.convention = convention if debug and not quiet: diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 9a0d355c1..7ff916b08 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -18,7 +18,7 @@ from nilearn.masking import compute_epi_mask from tedana import (decay, combine, decomposition, io, metrics, - reporting, selection, utils, __version__) + reporting, selection, utils, constants, __version__) import tedana.gscontrol as gsc from tedana.stats import computefeats2 from tedana.workflows.parser_utils import is_valid_file, check_tedpca_value, ContextFilter @@ -348,6 +348,8 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, if prefix and prefix[-1] != '_': prefix += '_' io.prefix = prefix + if convention == 'bids': + convention = constants.bids io.convention = convention # boilerplate From 7b0af33316d229db3852e809d9813ddc94484101 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 15 Mar 2021 11:32:19 -0400 Subject: [PATCH 12/39] Adds tsv and json tables --- tedana/constants.py | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tedana/constants.py b/tedana/constants.py index 851ea4f53..cd69df5a0 100644 --- a/tedana/constants.py +++ b/tedana/constants.py @@ -147,3 +147,57 @@ 'bidsv1.5.0': 'desc-ICAAcceptedMIRDenoised_components', }, } + +json_table = { + 'data description': { + 'orig': 'dataset_description', + 'bidsv1.5.0': 'dataset_description', + }, + 'PCA decomposition': { + 'orig': 'pca_decomposition', + 'bidsv1.5.0': 'desc-PCA_decomposition', + }, + 'PCA metrics': { + 'orig': 'pca_metrics', + 'bidsv1.5.0': 'desc-PCA_metrics', + }, + 'ICA decomposition': { + 'orig': 'ica_decomposition', + 'bidsv1.5.0': 'desc-ICA_decomposition', + }, + 'ICA metrics': { + 'orig': 'ica_metrics', + 'bidsv1.5.0': 'desc-tedana_metrics', + }, +} + +tsv_table = { + 'PCA mixing': { + 'orig': 'pca_mixing', + 'bidsv1.5.0': 'desc-PCA_mixing', + }, + 'PCA metrics': { + 'orig': 'pca_metrics', + 'bidsv1.5.0': 'desc-PCA_metrics', + }, + 'ICA mixing': { + 'orig': 'ica_mixing', + 'bidsv1.5.0': 'desc-ICA_mixing', + }, + 'ICA metrics': { + 'orig': 'ica_metrics', + 'bidsv1.5.0': 'desc-tedana_metrics', + }, + 'global signal time series': { + 'orig': 'global_signal_ts', + 'bidsv1.5.0': 'desc-globalSignal_timeseries', + }, + 'ICA MIR mixing': { + 'orig': 'ica_mir_mixing', + 'bidsv1.5.0': 'desc-ICAMIRDenoised_mixing', + }, + 'ICA orthogonalized mixing': { + 'orig': 'ica_orth_mixing', + 'bidsv1.5.0': 'desc-ICAOrth_mixing', + }, +} From e479836ed5c4b584a94e0ab446ae6b0563fd36fa Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 15 Mar 2021 13:46:09 -0400 Subject: [PATCH 13/39] Adds tables for json/tsv --- tedana/decomposition/pca.py | 26 +++++----- tedana/gscontrol.py | 5 +- tedana/io.py | 86 ++++++++++++++++++++++++++++----- tedana/reporting/html_report.py | 5 +- tedana/workflows/t2smap.py | 2 +- tedana/workflows/tedana.py | 32 ++++++------ 6 files changed, 107 insertions(+), 49 deletions(-) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 68b9d4512..231cf9fba 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -3,7 +3,6 @@ """ import json import logging -import os.path as op from numbers import Number import numpy as np @@ -148,19 +147,16 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, Outputs: This function writes out several files: - - =========================== ============================================= - Filename Content - =========================== ============================================= - desc-PCA_decomposition.json PCA component table - desc-PCA_mixing.tsv PCA mixing matrix - desc-PCA_components.nii.gz Component weight maps - =========================== ============================================= + - PCA component table + - PCA mixing matrix + - z-scored PCA components See Also -------- - :func:`tedana.utils.make_adaptive_mask` : The function used to create the ``adaptive_mask`` - parameter. + :func:`tedana.utils.make_adaptive_mask` : The function used to create + the ``adaptive_mask` parameter. + :module: `tedana.constants` : The module describing the filenames for + various naming conventions """ if algorithm == 'kundu': alg_str = ("followed by the Kundu component selection decision " @@ -278,12 +274,12 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, for comp in comptable.index.values] mixing_df = pd.DataFrame(data=comp_ts, columns=comp_names) - mixing_df.to_csv(op.join(out_dir, 'desc-PCA_mixing.tsv'), sep='\t', index=False) + mixing_df.to_csv(io.gen_tsv_name("PCA mixing"), sep='\t', index=False) # Save component table and associated json temp_comptable = comptable.set_index("Component", inplace=False) temp_comptable.to_csv( - op.join(out_dir, "desc-PCA_metrics.tsv"), + io.gen_tsv_name("PCA metrics"), index=True, index_label="Component", sep='\t', @@ -295,7 +291,7 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, "This identifier matches column names in the mixing matrix TSV file." ), } - with open(op.join(out_dir, "desc-PCA_metrics.json"), "w") as fo: + with open(io.gen_json_name("PCA metrics"), "w") as fo: json.dump(metric_metadata, fo, sort_keys=True, indent=4) decomp_metadata = { @@ -310,7 +306,7 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, "Description": "PCA fit to optimally combined data.", "Method": "tedana", } - with open(op.join(out_dir, "desc-PCA_decomposition.json"), "w") as fo: + with open(io.gen_json_name("PCA decomposition"), "w") as fo: json.dump(decomp_metadata, fo, sort_keys=True, indent=4) acc = comptable[comptable.classification == 'accepted'].index.values diff --git a/tedana/gscontrol.py b/tedana/gscontrol.py index a880cb3f7..3d74312bb 100644 --- a/tedana/gscontrol.py +++ b/tedana/gscontrol.py @@ -2,7 +2,6 @@ Global signal control methods """ import logging -import os.path as op import numpy as np import pandas as pd @@ -91,7 +90,7 @@ def gscontrol_raw(catd, optcom, n_echos, ref_img, out_dir='.', dtrank=4): glsig = stats.zscore(glsig, axis=None) glsig_df = pd.DataFrame(data=glsig.T, columns=['global_signal']) - glsig_df.to_csv(op.join(out_dir, 'desc-globalSignal_timeseries.tsv'), + glsig_df.to_csv(io.gen_tsv_name("global signal time series"), sep='\t', index=False) glbase = np.hstack([Lmix, glsig.T]) @@ -264,4 +263,4 @@ def minimum_image_regression(optcom_ts, mmix, mask, comptable, ref_img, out_dir= ref_img, ) mixing_df = pd.DataFrame(data=mmix_noT1gs.T, columns=comptable["Component"].values) - mixing_df.to_csv(op.join(out_dir, "desc-ICAMIRDenoised_mixing.tsv"), sep='\t', index=False) + mixing_df.to_csv(io.gen_tsv_name("ICA MIR mixing"), sep='\t', index=False) diff --git a/tedana/io.py b/tedana/io.py index 82633db97..3acb76d99 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -11,7 +11,9 @@ from tedana import utils from tedana.stats import computefeats2, get_coeffs -from .constants import allowed_conventions, img_table, bids +from .constants import ( + bids, allowed_conventions, img_table, json_table, tsv_table +) LGR = logging.getLogger(__name__) @@ -23,14 +25,27 @@ convention = bids # overridden in API or CLI calls -def gen_img_name(img_type, echo=0): +def check_convention() -> None: + """Checks set convention for io module + + Raises + ------ + RuntimeError, if invalid convention is set """ - Generates an image file full path to simplify file output + if convention not in allowed_conventions: + raise RuntimeError( + ('Convention %s is not valid; allowed: ' % convention) + + str(allowed_conventions) + ) + + +def gen_img_name(img_type: str, echo: str = 0) -> str: + """Generates an image file full path to simplify file output Parameters ---------- img_type : str - The description of the image. Must be a key in io.img_table + The description of the image. Must be a key in constants.img_table echo : :obj: `int` The echo number of the image. @@ -40,22 +55,17 @@ def gen_img_name(img_type, echo=0): Raises ------ - KeyError, if an invalid description is supplied - RuntimeError, if io has no convention set + KeyError, if an invalid description is supplied or API convention is + illegal ValueError, if an echo is supplied when it shouldn't be See Also -------- - io.img_table, a dict for translating various naming types + constants.img_table, a dict for translating various naming types """ - if not convention: - raise RuntimeError('Convention not set') - key = convention - if key not in allowed_conventions: - raise RuntimeError('Convention %s not allowed' % convention) if echo: img_type += ' split' - format_string = img_table[img_type][key] + format_string = img_table[img_type][convention] if echo and not ('{' in format_string): raise ValueError('Echo supplied when not supported!') elif echo: @@ -65,6 +75,56 @@ def gen_img_name(img_type, echo=0): return op.join(outdir, prefix + basename) +def gen_json_name(json_type: str) -> str: + """Generates a JSON file full path to simplify file output + + Parameters + ---------- + json_type: str + The description of the JSON. Must be a key in constants.json_table + + Returns + ------- + The full path for the JSON name + + Raises + ------ + KeyError, if an invalid description is supplied or API convention is + illegal + + See Also + -------- + constants.json_table, a dict for translating various json naming types + """ + basename = json_table[json_type][convention] + return op.join(outdir, prefix + basename + '.json') + + +def gen_tsv_name(tsv_type: str) -> str: + """Generates a TSV file full path to simplify file output + + Parameters + ---------- + tsv_type: str + The description of the TSV. Must be a key in constants.tsv_table + + Returns + ------- + The full path for the TSV name + + Raises + ------ + KeyError, if an invalid description is supplied or API convention is + illegal + + See Also + -------- + constants.tsv_table, a dict for translating various tsv naming types + """ + basename = tsv_table[tsv_type][convention] + return op.join(outdir, prefix + basename + '.tsv') + + def split_ts(data, mmix, mask, comptable): """ Splits `data` time series into accepted component time series and remainder diff --git a/tedana/reporting/html_report.py b/tedana/reporting/html_report.py index 583fcdac4..848793bdc 100644 --- a/tedana/reporting/html_report.py +++ b/tedana/reporting/html_report.py @@ -5,6 +5,7 @@ from string import Template from tedana.info import __version__ from tedana.reporting import dynamic_figures as df +from tedana.io import gen_tsv_name def _update_template_bokeh(bokeh_id, about, bokeh_js): @@ -70,12 +71,12 @@ def generate_report(out_dir, tr): A generated HTML report """ # Load the component time series - comp_ts_path = opj(out_dir, 'desc-ICA_mixing.tsv') + comp_ts_path = gen_tsv_name("ICA mixing") comp_ts_df = pd.read_csv(comp_ts_path, sep='\t', encoding='utf=8') n_vols, n_comps = comp_ts_df.shape # Load the component table - comptable_path = opj(out_dir, 'desc-tedana_metrics.tsv') + comptable_path = gen_tsv_name("ICA metrics") comptable_cds = df._create_data_struct(comptable_path) # Create kappa rho plot diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index 12edcab4b..52e034470 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -278,7 +278,7 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, } ] } - with open(op.join(out_dir, "dataset_description.json"), "w") as fo: + with open(io.gen_json_name('data description'), "w") as fo: json.dump(derivative_metadata, fo, sort_keys=True, indent=4) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 7ff916b08..231635eb5 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -437,8 +437,9 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, if mixm is not None and op.isfile(mixm): mixm = op.abspath(mixm) # Allow users to re-run on same folder - if mixm != op.join(out_dir, 'desc-ICA_mixing.tsv'): - shutil.copyfile(mixm, op.join(out_dir, 'desc-ICA_mixing.tsv')) + mixing_name = io.gen_tsv_name("ICA mixing") + if mixm != mixing_name: + shutil.copyfile(mixm, mixing_name) shutil.copyfile(mixm, op.join(out_dir, op.basename(mixm))) elif mixm is not None: raise IOError('Argument "mixm" must be an existing file.') @@ -446,8 +447,9 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, if ctab is not None and op.isfile(ctab): ctab = op.abspath(ctab) # Allow users to re-run on same folder - if ctab != op.join(out_dir, 'desc-tedana_metrics.tsv'): - shutil.copyfile(ctab, op.join(out_dir, 'desc-tedana_metrics.tsv')) + metrics_name = io.gen_tsv_name("ICA metrics") + if ctab != metrics_name: + shutil.copyfile(ctab, metrics_name) shutil.copyfile(ctab, op.join(out_dir, op.basename(ctab))) elif ctab is not None: raise IOError('Argument "ctab" must be an existing file.') @@ -589,13 +591,14 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, keep_restarting = False else: LGR.info('Using supplied mixing matrix from ICA') - mmix_orig = pd.read_table(op.join(out_dir, 'desc-ICA_mixing.tsv')).values + mixing_file = io.gen_tsv_name("ICA mixing") + mmix_orig = pd.read_table(mixing_file).values if ctab is None: comptable, metric_maps, metric_metadata, betas, mmix = metrics.dependence_metrics( catd, data_oc, mmix_orig, masksum, tes, - ref_img, label='ICA', out_dir=out_dir, - algorithm='kundu_v2', verbose=verbose) + ref_img, label='ICA', algorithm='kundu_v2', + verbose=verbose) comptable, metric_metadata = metrics.kundu_metrics( comptable, metric_maps, @@ -611,8 +614,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, mmix = mmix_orig.copy() comptable = pd.read_table(ctab) # Try to find and load the metric metadata file - ctab_parts = ctab.split(".") - metadata_file = ctab_parts[0] + ".json" + metadata_file = io.gen_json_name('ICA metrics') if op.isfile(metadata_file): with open(metadata_file, "r") as fo: metric_metadata = json.load(fo) @@ -629,14 +631,14 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, # Write out ICA files. comp_names = comptable["Component"].values mixing_df = pd.DataFrame(data=mmix, columns=comp_names) - mixing_df.to_csv(op.join(out_dir, "desc-ICA_mixing.tsv"), sep="\t", index=False) + mixing_df.to_csv(io.gen_tsv_name("ICA mixing"), sep="\t", index=False) betas_oc = utils.unmask(computefeats2(data_oc, mmix, mask), mask) io.filewrite(betas_oc, 'z-scored ICA components', ref_img) # Save component table and associated json temp_comptable = comptable.set_index("Component", inplace=False) temp_comptable.to_csv( - op.join(out_dir, "desc-tedana_metrics.tsv"), + io.gen_tsv_name("ICA metrics"), index=True, index_label="Component", sep='\t', @@ -648,7 +650,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, "This identifier matches column names in the mixing matrix TSV file." ), } - with open(op.join(out_dir, "desc-tedana_metrics.json"), "w") as fo: + with open(io.gen_json_name("ICA metrics"), "w") as fo: json.dump(metric_metadata, fo, sort_keys=True, indent=4) decomp_metadata = { @@ -665,7 +667,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, "Description": "ICA fit to dimensionally-reduced optimally combined data.", "Method": "tedana", } - with open(op.join(out_dir, "desc-ICA_decomposition.json"), "w") as fo: + with open(io.gen_json_name("ICA decomposition"), "w") as fo: json.dump(decomp_metadata, fo, sort_keys=True, indent=4) if comptable[comptable.classification == 'accepted'].shape[0] == 0: @@ -688,7 +690,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, for comp in comptable.index.values] mixing_df = pd.DataFrame(data=mmix, columns=comp_names) mixing_df.to_csv( - op.join(out_dir, 'desc-ICAOrth_mixing.tsv'), + io.gen_tsv_name("ICA orthogonalized mixing"), sep='\t', index=False ) @@ -749,7 +751,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, } ] } - with open(op.join(out_dir, "dataset_description.json"), "w") as fo: + with open(io.gen_json_name("data description"), "w") as fo: json.dump(derivative_metadata, fo, sort_keys=True, indent=4) LGR.info('Workflow completed') From 1135968871d276426621548b67d8b8f6e31c6b38 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 16 Mar 2021 11:06:06 -0400 Subject: [PATCH 14/39] Address @handwerkerd comments --- tedana/io.py | 3 +++ tedana/workflows/t2smap.py | 2 +- tedana/workflows/tedana.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index 3acb76d99..a3a2a8e66 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -63,6 +63,7 @@ def gen_img_name(img_type: str, echo: str = 0) -> str: -------- constants.img_table, a dict for translating various naming types """ + check_convention() if echo: img_type += ' split' format_string = img_table[img_type][convention] @@ -96,6 +97,7 @@ def gen_json_name(json_type: str) -> str: -------- constants.json_table, a dict for translating various json naming types """ + check_convention() basename = json_table[json_type][convention] return op.join(outdir, prefix + basename + '.json') @@ -121,6 +123,7 @@ def gen_tsv_name(tsv_type: str) -> str: -------- constants.tsv_table, a dict for translating various tsv naming types """ + check_convention() basename = tsv_table[tsv_type][convention] return op.join(outdir, prefix + basename + '.tsv') diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index 52e034470..a6cd7e398 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -72,7 +72,7 @@ def _get_parser(): optional.add_argument('--convention', dest='convention', action='store', - choices=['kundu', 'bids'], + choices=['orig', 'bids'], help=('Filenaming convention. bids will use ' 'the latest BIDS derivatives version.'), default='bids') diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 231635eb5..3623dbe7f 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -86,7 +86,7 @@ def _get_parser(): optional.add_argument('--convention', dest='convention', action='store', - choices=['kundu', 'bids'], + choices=['orig', 'bids'], help=("Filenaming convention. bids will use " "the latest BIDS derivatives version."), default='bids') From 01ceae960f586d5f3f3fc14b5b4beee4b497842e Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Wed, 17 Mar 2021 12:50:50 -0400 Subject: [PATCH 15/39] Add documentation and rearrange module --- tedana/io.py | 257 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 160 insertions(+), 97 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index a3a2a8e66..13c65ca70 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -1,5 +1,63 @@ """ -Functions to handle file input/output +============================= +io module (:mod: `tedana.io`) +============================= + +.. currentmodule:: tedana.io + +The io module handles most file input and output in the `tedana` workflow, +and simplifies some naming function calls with module globals (see "Globals" +and "Notes" below). Other functions in the module help simplify writing out +data from multiple echoes or write very complex outputs. + + +Globals +------- +outdir +prefix +convention + + +Naming Functions +---------------- +get_img_name +get_json_name +get_tsv_name +add_decomp_prefix + + +File Writing Functions +---------------------- +write_split_ts +writefeats +writeresults +writeresults_echoes +filewrite + + +File Loading Functions +---------------------- +load_data + + +Helper Functions +---------------- +new_nii_like +split_ts +check_convention + +See Also +-------- +`tedana.constants` + + +Notes +----- +The global variables are set by default in the module to guarantee that the +functions that use them won't fail if a workflow API is not used. +However, API calls can override the default settings. Additionally, the +naming functions beginning with "get" all leverage dictionaries defined in +the `constants.py` module, as the definitions are large. """ import logging import os.path as op @@ -24,19 +82,7 @@ prefix = '' convention = bids # overridden in API or CLI calls - -def check_convention() -> None: - """Checks set convention for io module - - Raises - ------ - RuntimeError, if invalid convention is set - """ - if convention not in allowed_conventions: - raise RuntimeError( - ('Convention %s is not valid; allowed: ' % convention) + - str(allowed_conventions) - ) +# Naming Functions def gen_img_name(img_type: str, echo: str = 0) -> str: @@ -128,46 +174,34 @@ def gen_tsv_name(tsv_type: str) -> str: return op.join(outdir, prefix + basename + '.tsv') -def split_ts(data, mmix, mask, comptable): +def add_decomp_prefix(comp_num, prefix, max_value): """ - Splits `data` time series into accepted component time series and remainder + Create component name with leading zeros matching number of components Parameters ---------- - data : (S x T) array_like - Input data, where `S` is samples and `T` is time - mmix : (T x C) array_like - Mixing matrix for converting input data to component space, where `C` - is components and `T` is the same as in `data` - mask : (S,) array_like - Boolean mask array - comptable : (C x X) :obj:`pandas.DataFrame` - Component metric table. One row for each component, with a column for - each metric. Requires at least two columns: "component" and - "classification". + comp_num : :obj:`int` + Component number + prefix : :obj:`str` + A prefix to prepend to the component name. An underscore is + automatically added between the prefix and the component number. + max_value : :obj:`int` + The maximum component number in the whole decomposition. Used to + determine the appropriate number of leading zeros in the component + name. Returns ------- - hikts : (S x T) :obj:`numpy.ndarray` - Time series reconstructed using only components in `acc` - resid : (S x T) :obj:`numpy.ndarray` - Original data with `hikts` removed + comp_name : :obj:`str` + Component name in the form _ """ - acc = comptable[comptable.classification == 'accepted'].index.values - - cbetas = get_coeffs(data - data.mean(axis=-1, keepdims=True), - mmix, mask) - betas = cbetas[mask] - if len(acc) != 0: - hikts = utils.unmask(betas[:, acc].dot(mmix.T[acc, :]), mask) - else: - hikts = None - - resid = data - hikts - - return hikts, resid + n_digits = int(np.log10(max_value)) + 1 + comp_name = '{0:08d}'.format(int(comp_num)) + comp_name = '{0}_{1}'.format(prefix, comp_name[8 - n_digits:]) + return comp_name +# File Writing Functions def write_split_ts(data, mmix, mask, comptable, ref_img, echo=0): """ Splits `data` into denoised / noise / ignored time series and saves to disk @@ -391,42 +425,6 @@ def writeresults_echoes(catd, mmix, mask, comptable, ref_img): ) -def new_nii_like(ref_img, data, affine=None, copy_header=True): - """ - Coerces `data` into NiftiImage format like `ref_img` - - Parameters - ---------- - ref_img : :obj:`str` or img_like - Reference image - data : (S [x T]) array_like - Data to be saved - affine : (4 x 4) array_like, optional - Transformation matrix to be used. Default: `ref_img.affine` - copy_header : :obj:`bool`, optional - Whether to copy header from `ref_img` to new image. Default: True - - Returns - ------- - nii : :obj:`nibabel.nifti1.Nifti1Image` - NiftiImage - """ - - ref_img = check_niimg(ref_img) - newdata = data.reshape(ref_img.shape[:3] + data.shape[1:]) - if '.nii' not in ref_img.valid_exts: - # this is rather ugly and may lose some information... - nii = nib.Nifti1Image(newdata, affine=ref_img.affine, - header=ref_img.header) - else: - # nilearn's `new_img_like` is a very nice function - nii = new_img_like(ref_img, newdata, affine=affine, - copy_header=copy_header) - nii.set_data_dtype(data.dtype) - - return nii - - def filewrite(data, img_type, ref_img, gzip=True, copy_header=True, echo=0): """ @@ -470,6 +468,7 @@ def filewrite(data, img_type, ref_img, gzip=True, copy_header=True, return name +# File Loading Functions def load_data(data, n_echos=None): """ Coerces input `data` files to required 3D array output @@ -520,28 +519,92 @@ def load_data(data, n_echos=None): return fdata, ref_img -def add_decomp_prefix(comp_num, prefix, max_value): +# Helper Functions +def new_nii_like(ref_img, data, affine=None, copy_header=True): """ - Create component name with leading zeros matching number of components + Coerces `data` into NiftiImage format like `ref_img` Parameters ---------- - comp_num : :obj:`int` - Component number - prefix : :obj:`str` - A prefix to prepend to the component name. An underscore is - automatically added between the prefix and the component number. - max_value : :obj:`int` - The maximum component number in the whole decomposition. Used to - determine the appropriate number of leading zeros in the component - name. + ref_img : :obj:`str` or img_like + Reference image + data : (S [x T]) array_like + Data to be saved + affine : (4 x 4) array_like, optional + Transformation matrix to be used. Default: `ref_img.affine` + copy_header : :obj:`bool`, optional + Whether to copy header from `ref_img` to new image. Default: True Returns ------- - comp_name : :obj:`str` - Component name in the form _ + nii : :obj:`nibabel.nifti1.Nifti1Image` + NiftiImage """ - n_digits = int(np.log10(max_value)) + 1 - comp_name = '{0:08d}'.format(int(comp_num)) - comp_name = '{0}_{1}'.format(prefix, comp_name[8 - n_digits:]) - return comp_name + + ref_img = check_niimg(ref_img) + newdata = data.reshape(ref_img.shape[:3] + data.shape[1:]) + if '.nii' not in ref_img.valid_exts: + # this is rather ugly and may lose some information... + nii = nib.Nifti1Image(newdata, affine=ref_img.affine, + header=ref_img.header) + else: + # nilearn's `new_img_like` is a very nice function + nii = new_img_like(ref_img, newdata, affine=affine, + copy_header=copy_header) + nii.set_data_dtype(data.dtype) + + return nii + + +def split_ts(data, mmix, mask, comptable): + """ + Splits `data` time series into accepted component time series and remainder + + Parameters + ---------- + data : (S x T) array_like + Input data, where `S` is samples and `T` is time + mmix : (T x C) array_like + Mixing matrix for converting input data to component space, where `C` + is components and `T` is the same as in `data` + mask : (S,) array_like + Boolean mask array + comptable : (C x X) :obj:`pandas.DataFrame` + Component metric table. One row for each component, with a column for + each metric. Requires at least two columns: "component" and + "classification". + + Returns + ------- + hikts : (S x T) :obj:`numpy.ndarray` + Time series reconstructed using only components in `acc` + resid : (S x T) :obj:`numpy.ndarray` + Original data with `hikts` removed + """ + acc = comptable[comptable.classification == 'accepted'].index.values + + cbetas = get_coeffs(data - data.mean(axis=-1, keepdims=True), + mmix, mask) + betas = cbetas[mask] + if len(acc) != 0: + hikts = utils.unmask(betas[:, acc].dot(mmix.T[acc, :]), mask) + else: + hikts = None + + resid = data - hikts + + return hikts, resid + + +def check_convention() -> None: + """Checks set convention for io module + + Raises + ------ + RuntimeError, if invalid convention is set + """ + if convention not in allowed_conventions: + raise RuntimeError( + ('Convention %s is not valid; allowed: ' % convention) + + str(allowed_conventions) + ) From 8df1331b8008d7542c17198232ffc0b54a50c284 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Thu, 18 Mar 2021 12:56:25 -0400 Subject: [PATCH 16/39] Adds module text for constants.py --- tedana/constants.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tedana/constants.py b/tedana/constants.py index cd69df5a0..1a9accf68 100644 --- a/tedana/constants.py +++ b/tedana/constants.py @@ -1,12 +1,42 @@ """ -Constants for tedana +=========================================== +constants module (:mod: `tedana.constants`) +=========================================== + +.. currentmodule:: tedana.io + +The constants module defines constants for use in the `tedana` package. +There are only variable definitions here, and no functions. + +Input and Output +---------------- +allowed_conventions + Defines the keys present in each of the "table" variables for i/o. + Each element represents a naming convention. +bids + A constant defining the string value of the current BIDS version +img_table + A table of images that may be written. Images that are split by echo + end in the word "split" and are formats rather than complete strings. +json_table + A table of JSON files that may be written. +tsv_table + A table of TSV files that may be written. + + +Notes +----- +For input and output constants ending in "table," the first key is the +type of file to be written (for example, 't2star map'). The second key +indicates the naming convention to be used (for example, 'orig'). If an +invalid type of file or convention is used, an ambiguous KeyError will +occur. """ allowed_conventions = ('orig', 'bidsv1.5.0') bids = 'bidsv1.5.0' -# filename tables img_table = { 'adaptive mask': { 'orig': 'adaptive_mask', From d5ce0f393d64408b7f15dfbfcb1359566b664b1f Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Thu, 18 Mar 2021 16:52:54 -0400 Subject: [PATCH 17/39] Adds set_convention --- tedana/io.py | 42 +++++++++++++++++++------------------- tedana/tests/test_io.py | 2 +- tedana/workflows/t2smap.py | 4 +--- tedana/workflows/tedana.py | 4 +--- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index 13c65ca70..c0e057d1b 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -20,9 +20,10 @@ Naming Functions ---------------- -get_img_name -get_json_name -get_tsv_name +set_convention +gen_img_name +gen_json_name +gen_tsv_name add_decomp_prefix @@ -44,7 +45,6 @@ ---------------- new_nii_like split_ts -check_convention See Also -------- @@ -83,7 +83,24 @@ convention = bids # overridden in API or CLI calls # Naming Functions +def set_convention(name: str) -> None: + """Sets the convention for the io module + Parameters + ---------- + name: str in ('orig', 'bidsv1.5.0', 'bids') + The convention name to set this module for + + Notes + ----- + Uses the `io.convention` module-wide variable + """ + if name in allowed_conventions: + convention = name + elif name == 'bids': + convention = bids + else: + raise ValueError('Convention %s is invalid' % name) def gen_img_name(img_type: str, echo: str = 0) -> str: """Generates an image file full path to simplify file output @@ -109,7 +126,6 @@ def gen_img_name(img_type: str, echo: str = 0) -> str: -------- constants.img_table, a dict for translating various naming types """ - check_convention() if echo: img_type += ' split' format_string = img_table[img_type][convention] @@ -143,7 +159,6 @@ def gen_json_name(json_type: str) -> str: -------- constants.json_table, a dict for translating various json naming types """ - check_convention() basename = json_table[json_type][convention] return op.join(outdir, prefix + basename + '.json') @@ -169,7 +184,6 @@ def gen_tsv_name(tsv_type: str) -> str: -------- constants.tsv_table, a dict for translating various tsv naming types """ - check_convention() basename = tsv_table[tsv_type][convention] return op.join(outdir, prefix + basename + '.tsv') @@ -594,17 +608,3 @@ def split_ts(data, mmix, mask, comptable): resid = data - hikts return hikts, resid - - -def check_convention() -> None: - """Checks set convention for io module - - Raises - ------ - RuntimeError, if invalid convention is set - """ - if convention not in allowed_conventions: - raise RuntimeError( - ('Convention %s is not valid; allowed: ' % convention) + - str(allowed_conventions) - ) diff --git a/tedana/tests/test_io.py b/tedana/tests/test_io.py index df873fcbf..6be2b1692 100644 --- a/tedana/tests/test_io.py +++ b/tedana/tests/test_io.py @@ -148,7 +148,7 @@ def test_smoke_filewrite(): me.filewrite(data_1d, '', ref_img) for convention in (constants.bids, 'orig'): - me.convention = convention + me.set_convention(convention) fname = me.filewrite(data_1d, 't2star map', ref_img) assert fname is not None try: diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index a6cd7e398..d2579b4e2 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -193,9 +193,7 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, os.mkdir(out_dir) io.outdir = out_dir io.prefix = prefix - if convention == 'bids': - convention = constants.bids - io.convention = convention + io.set_convention(convention) if debug and not quiet: logging.basicConfig(level=logging.DEBUG) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 3623dbe7f..93b2ff0f9 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -348,9 +348,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, if prefix and prefix[-1] != '_': prefix += '_' io.prefix = prefix - if convention == 'bids': - convention = constants.bids - io.convention = convention + io.set_convention(convention) # boilerplate basename = 'report' From 61ba799c246daf7e944ba347b514a8344b3da2f4 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Thu, 18 Mar 2021 17:00:18 -0400 Subject: [PATCH 18/39] Adds set_prefix function --- tedana/io.py | 20 ++++++++++++++++++++ tedana/workflows/t2smap.py | 2 +- tedana/workflows/tedana.py | 4 +--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index c0e057d1b..7d42fc661 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -21,6 +21,7 @@ Naming Functions ---------------- set_convention +set_prefix gen_img_name gen_json_name gen_tsv_name @@ -94,6 +95,10 @@ def set_convention(name: str) -> None: Notes ----- Uses the `io.convention` module-wide variable + + Raises + ------ + ValueError if the name is not valid """ if name in allowed_conventions: convention = name @@ -102,6 +107,21 @@ def set_convention(name: str) -> None: else: raise ValueError('Convention %s is invalid' % name) + +def set_prefix(pref: str) -> None: + """Sets the prefix for the io module + + Parameters + ---------- + pref: str + The prefix to set for the module. If the prefix is not blank, + filenames will have the prefix and underscore before all filenames + """ + if pref: + pref += '_' + prefix = pref + + def gen_img_name(img_type: str, echo: str = 0) -> str: """Generates an image file full path to simplify file output diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index d2579b4e2..79f093154 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -192,7 +192,7 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, if not op.isdir(out_dir): os.mkdir(out_dir) io.outdir = out_dir - io.prefix = prefix + io.set_prefix(prefix) io.set_convention(convention) if debug and not quiet: diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 93b2ff0f9..1314bfad8 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -345,9 +345,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, os.mkdir(out_dir) io.outdir = out_dir - if prefix and prefix[-1] != '_': - prefix += '_' - io.prefix = prefix + io.set_prefix(prefix) io.set_convention(convention) # boilerplate From e55a5caf900b71b3301401a3a85947d88e2fd1d4 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Thu, 18 Mar 2021 17:09:31 -0400 Subject: [PATCH 19/39] Fixes style --- tedana/io.py | 7 ++++--- tedana/workflows/t2smap.py | 2 +- tedana/workflows/tedana.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index 7d42fc661..a1f64a5f7 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -79,9 +79,10 @@ RepLGR = logging.getLogger('REPORT') RefLGR = logging.getLogger('REFERENCES') -outdir = '.' -prefix = '' -convention = bids # overridden in API or CLI calls +global outdir = '.' +global prefix = '' +global convention = bids # overridden in API or CLI calls + # Naming Functions def set_convention(name: str) -> None: diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index 79f093154..3ec9a2746 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -11,7 +11,7 @@ from scipy import stats from threadpoolctl import threadpool_limits -from tedana import (combine, decay, io, utils, constants, __version__) +from tedana import (combine, decay, io, utils, __version__) from tedana.workflows.parser_utils import is_valid_file LGR = logging.getLogger(__name__) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 1314bfad8..ac3e47d6b 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -18,7 +18,7 @@ from nilearn.masking import compute_epi_mask from tedana import (decay, combine, decomposition, io, metrics, - reporting, selection, utils, constants, __version__) + reporting, selection, utils, __version__) import tedana.gscontrol as gsc from tedana.stats import computefeats2 from tedana.workflows.parser_utils import is_valid_file, check_tedpca_value, ContextFilter From 0f52846c711e6a7c1b4a8241d723ed4548ed1d6f Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Thu, 18 Mar 2021 17:18:48 -0400 Subject: [PATCH 20/39] Actually fix things --- tedana/io.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index a1f64a5f7..e638e9342 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -79,9 +79,9 @@ RepLGR = logging.getLogger('REPORT') RefLGR = logging.getLogger('REFERENCES') -global outdir = '.' -global prefix = '' -global convention = bids # overridden in API or CLI calls +outdir = '.' +prefix = '' +convention = bids # overridden in API or CLI calls # Naming Functions @@ -107,6 +107,7 @@ def set_convention(name: str) -> None: convention = bids else: raise ValueError('Convention %s is invalid' % name) + LGR.info('Set convention as %s' % convention) def set_prefix(pref: str) -> None: @@ -121,6 +122,7 @@ def set_prefix(pref: str) -> None: if pref: pref += '_' prefix = pref + LGR.info('Set prefix as %s' % prefix) def gen_img_name(img_type: str, echo: str = 0) -> str: From 7b955b238ec99abb2c885db6c3422adf84d1204a Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 09:34:51 -0400 Subject: [PATCH 21/39] Clarifies PCA docstring --- tedana/decomposition/pca.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 231cf9fba..22fdcbf74 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -147,9 +147,13 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, Outputs: This function writes out several files: - - PCA component table - - PCA mixing matrix - - z-scored PCA components + =========================== ============================================= + Default Filename Content + =========================== ============================================= + desc-PCA_decomposition.json PCA component table + desc-PCA_mixing.tsv PCA mixing matrix + desc-PCA_components.nii.gz Component weight maps + =========================== ============================================= See Also -------- From b93071a25dcb879e878441b5699e43e3c637fd8c Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 13:15:28 -0400 Subject: [PATCH 22/39] Update tedana/decomposition/pca.py Co-authored-by: Taylor Salo --- tedana/decomposition/pca.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/decomposition/pca.py b/tedana/decomposition/pca.py index 231cf9fba..e624c3114 100644 --- a/tedana/decomposition/pca.py +++ b/tedana/decomposition/pca.py @@ -155,7 +155,7 @@ def tedpca(data_cat, data_oc, combmode, mask, adaptive_mask, t2sG, -------- :func:`tedana.utils.make_adaptive_mask` : The function used to create the ``adaptive_mask` parameter. - :module: `tedana.constants` : The module describing the filenames for + :module:`tedana.constants` : The module describing the filenames for various naming conventions """ if algorithm == 'kundu': From 5fdcd88504833186ef8ae3b90d0fb0a1c75d2379 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 13:15:36 -0400 Subject: [PATCH 23/39] Update tedana/io.py Co-authored-by: Taylor Salo --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index e638e9342..71fdfe09c 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -90,7 +90,7 @@ def set_convention(name: str) -> None: Parameters ---------- - name: str in ('orig', 'bidsv1.5.0', 'bids') + name : {'orig', 'bidsv1.5.0', 'bids'} The convention name to set this module for Notes From cdbe1e457eea921e79a1eb52f5a0273ac56f8937 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 13:15:44 -0400 Subject: [PATCH 24/39] Update tedana/io.py Co-authored-by: Taylor Salo --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index 71fdfe09c..cf02a55c8 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -125,7 +125,7 @@ def set_prefix(pref: str) -> None: LGR.info('Set prefix as %s' % prefix) -def gen_img_name(img_type: str, echo: str = 0) -> str: +def gen_img_name(img_type: str, echo: int = 0) -> str: """Generates an image file full path to simplify file output Parameters From 6d93468dd33804a676c2c5a3c356378015c534fe Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 13:15:51 -0400 Subject: [PATCH 25/39] Update tedana/io.py Co-authored-by: Taylor Salo --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index cf02a55c8..fc122bb28 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -115,7 +115,7 @@ def set_prefix(pref: str) -> None: Parameters ---------- - pref: str + pref : str The prefix to set for the module. If the prefix is not blank, filenames will have the prefix and underscore before all filenames """ From 02726df5e9e5fb23df5cb98c43bfb8938490992e Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 13:15:58 -0400 Subject: [PATCH 26/39] Update tedana/io.py Co-authored-by: Taylor Salo --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index fc122bb28..cc0d3ed42 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -133,7 +133,7 @@ def gen_img_name(img_type: str, echo: int = 0) -> str: img_type : str The description of the image. Must be a key in constants.img_table echo : :obj: `int` - The echo number of the image. + The echo number of the image. Default is 0. Returns ------- From c349c0d08f69c5b47c36d7071933104d3dc96a04 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 13:17:59 -0400 Subject: [PATCH 27/39] Update tedana/io.py Co-authored-by: Taylor Salo --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index cc0d3ed42..124b24b6d 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -132,7 +132,7 @@ def gen_img_name(img_type: str, echo: int = 0) -> str: ---------- img_type : str The description of the image. Must be a key in constants.img_table - echo : :obj: `int` + echo : :obj:`int`, optional The echo number of the image. Default is 0. Returns From 63903a4868e3a2eb895ebde47eb0d9af47981814 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 14:50:32 -0400 Subject: [PATCH 28/39] Addresses some review concerns --- tedana/io.py | 25 ++++++++----------------- tedana/workflows/t2smap.py | 8 +------- tedana/workflows/tedana.py | 8 +------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index 124b24b6d..2c57c9253 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -14,14 +14,12 @@ Globals ------- outdir -prefix convention Naming Functions ---------------- set_convention -set_prefix gen_img_name gen_json_name gen_tsv_name @@ -79,8 +77,11 @@ RepLGR = logging.getLogger('REPORT') RefLGR = logging.getLogger('REFERENCES') +global outdir outdir = '.' +global prefix prefix = '' +global convention convention = bids # overridden in API or CLI calls @@ -93,6 +94,10 @@ def set_convention(name: str) -> None: name : {'orig', 'bidsv1.5.0', 'bids'} The convention name to set this module for + Returns + ------- + The valid string representation of a convention + Notes ----- Uses the `io.convention` module-wide variable @@ -101,6 +106,7 @@ def set_convention(name: str) -> None: ------ ValueError if the name is not valid """ + global convention if name in allowed_conventions: convention = name elif name == 'bids': @@ -110,21 +116,6 @@ def set_convention(name: str) -> None: LGR.info('Set convention as %s' % convention) -def set_prefix(pref: str) -> None: - """Sets the prefix for the io module - - Parameters - ---------- - pref : str - The prefix to set for the module. If the prefix is not blank, - filenames will have the prefix and underscore before all filenames - """ - if pref: - pref += '_' - prefix = pref - LGR.info('Set prefix as %s' % prefix) - - def gen_img_name(img_type: str, echo: int = 0) -> str: """Generates an image file full path to simplify file output diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index 3ec9a2746..a09d37573 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -64,11 +64,6 @@ def _get_parser(): 'Dependent ANAlysis. Must be in the same ' 'space as `data`.'), default=None) - optional.add_argument('--prefix', - dest='prefix', - type=str, - help='Prefix for filenames generated.', - default='') optional.add_argument('--convention', dest='convention', action='store', @@ -129,7 +124,7 @@ def _get_parser(): def t2smap_workflow(data, tes, out_dir='.', mask=None, - prefix='', convention='bids', + convention='bids', fittype='loglin', fitmode='all', combmode='t2s', debug=False, quiet=False): """ @@ -192,7 +187,6 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, if not op.isdir(out_dir): os.mkdir(out_dir) io.outdir = out_dir - io.set_prefix(prefix) io.set_convention(convention) if debug and not quiet: diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index ac3e47d6b..08ebe3c47 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -78,11 +78,6 @@ def _get_parser(): "function will be used to derive a mask " "from the first echo's data."), default=None) - optional.add_argument('--prefix', - dest='prefix', - type=str, - help="Prefix for filenames generated.", - default='') optional.add_argument('--convention', dest='convention', action='store', @@ -248,7 +243,7 @@ def _get_parser(): def tedana_workflow(data, tes, out_dir='.', mask=None, - convention='bids', prefix='', + convention='bids', fittype='loglin', combmode='t2s', tedpca='mdl', fixed_seed=42, maxit=500, maxrestart=10, tedort=False, gscontrol=None, @@ -345,7 +340,6 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, os.mkdir(out_dir) io.outdir = out_dir - io.set_prefix(prefix) io.set_convention(convention) # boilerplate From 482851d9a21fad3769138bceac18895c02d10ccf Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 16:50:06 -0400 Subject: [PATCH 29/39] Switches tables to json files --- tedana/config/img_table.json | 138 +++++++++++++++++++++++ tedana/config/json_table.json | 22 ++++ tedana/config/tsv_table.json | 30 +++++ tedana/constants.py | 200 ++-------------------------------- tedana/io.py | 33 +++++- 5 files changed, 228 insertions(+), 195 deletions(-) create mode 100644 tedana/config/img_table.json create mode 100644 tedana/config/json_table.json create mode 100644 tedana/config/tsv_table.json diff --git a/tedana/config/img_table.json b/tedana/config/img_table.json new file mode 100644 index 000000000..8dc27568e --- /dev/null +++ b/tedana/config/img_table.json @@ -0,0 +1,138 @@ +{ + "adaptive mask": { + "orig": "adaptive_mask", + "bidsv1.5.0": "desc-adaptiveGoodSignal_mask" + }, + "t2star map": { + "orig": "t2sv", + "bidsv1.5.0": "T2starmap" + }, + "s0 map": { + "orig": "s0v", + "bidsv1.5.0": "S0map" + }, + "combined": { + "orig": "ts_OC", + "bidsv1.5.0": "desc-optcom_bold" + }, + "ICA components": { + "orig": "ica_components", + "bidsv1.5.0": "desc-ICA_components" + }, + "z-scored PCA components": { + "orig": "pca_components", + "bidsv1.5.0": "desc-PCA_stat-z_components" + }, + "z-scored ICA components": { + "orig": "betas_OC", + "bidsv1.5.0": "desc-ICA_stat-z_components" + }, + "ICA accepted components": { + "orig": "betas_hik_OC", + "bidsv1.5.0": "desc-ICAAccepted_components" + }, + "z-scored ICA accepted components": { + "orig": "feats_OC2", + "bidsv1.5.0": "desc-ICAAccepted_stat-z_components" + }, + "denoised ts": { + "orig": "dn_ts_OC", + "bidsv1.5.0": "desc-optcomDenoised_bold" + }, + "high kappa ts": { + "orig": "hik_ts_OC", + "bidsv1.5.0": "desc-optcomAccepted_bold" + }, + "low kappa ts": { + "orig": "lowk_ts_OC", + "bidsv1.5.0": "desc-optcomRejected_bold" + }, + "full t2star map": { + "orig": "t2svG", + "bidsv1.5.0": "desc-full_T2starmap" + }, + "full s0 map": { + "orig": "s0vG", + "bidsv1.5.0": "desc-full_S0map" + }, + "whitened": { + "orig": "ts_OC_whitened", + "bidsv1.5.0": "desc-optcomPCAReduced_bold" + }, + "echo weight PCA map split": { + "orig": "e{0}_PCA_comp", + "bidsv1.5.0": "echo-{0}_desc-PCA_components" + }, + "echo R2 PCA split": { + "orig": "e{0}_PCA_R2", + "bidsv1.5.0": "echo-{0}_desc-PCAR2ModelPredictions_components" + }, + "echo S0 PCA split": { + "orig": "e{0}_PCA_S0", + "bidsv1.5.0": "echo-{0}_desc-PCAS0ModelPredictions_components" + }, + "PCA component weights": { + "orig": "pca_weights", + "bidsv1.5.0": "desc-PCAAveragingWeights_components" + }, + "PCA reduced": { + "orig": "oc_reduced", + "bidsv1.5.0": "desc-optcomPCAReduced_bold" + }, + "echo weight ICA map split": { + "orig": "e{0}_ICA_comp", + "bidsv1.5.0": "echo-{0}_desc-ICA_components" + }, + "echo R2 ICA split": { + "orig": "e{0}_ICA_R2", + "bidsv1.5.0": "echo-{0}_desc-ICAR2ModelPredictions_components" + }, + "echo S0 ICA split": { + "orig": "e{0}_ICA_S0", + "bidsv1.5.0": "echo-{0}_desc-ICAS0ModelPredictions_components" + }, + "ICA component weights": { + "orig": "ica_weights", + "bidsv1.5.0": "desc-ICAAveragingWeights_components" + }, + "high kappa ts split": { + "orig": "hik_ts_e{0}", + "bidsv1.5.0": "echo-{0}_desc-Accepted_bold" + }, + "low kappa ts split": { + "orig": "lowk_ts_e{0}", + "bidsv1.5.0": "echo-{0}_desc-Rejected_bold" + }, + "denoised ts split": { + "orig": "dn_ts_e{0}", + "bidsv1.5.0": "echo-{0}_desc-Denoised_bold" + }, + "gs map": { + "orig": "T1gs", + "bidsv1.5.0": "desc-globalSignal_map" + }, + "has gs combined": { + "orig": "tsoc_orig", + "bidsv1.5.0": "desc-optcomWithGlobalSignal_bold" + }, + "removed gs combined": { + "orig": "tsoc_nogs", + "bidsv1.5.0": "desc-optcomNoGlobalSignal_bold" + }, + "t1 like": { + "orig": "sphis_hik", + "bidsv1.5.0": "desc-T1likeEffect_min" + }, + "ICA accepted mir denoised": { + "orig": "hik_ts_OC_MIR", + "bidsv1.5.0": "desc-optcomAcceptedMIRDenoised_bold" + }, + "mir denoised": { + "orig": "dn_ts_OC_MIR", + "bidsv1.5.0": "desc-optcomMIRDenoised_bold" + }, + "ICA accepted mir component weights": { + "orig": "betas_hik_OC_MIR", + "bidsv1.5.0": "desc-ICAAcceptedMIRDenoised_components" + } +} diff --git a/tedana/config/json_table.json b/tedana/config/json_table.json new file mode 100644 index 000000000..b11f990a0 --- /dev/null +++ b/tedana/config/json_table.json @@ -0,0 +1,22 @@ +{ + "data description": { + "orig": "dataset_description", + "bidsv1.5.0": "dataset_description" + }, + "PCA decomposition": { + "orig": "pca_decomposition", + "bidsv1.5.0": "desc-PCA_decomposition" + }, + "PCA metrics": { + "orig": "pca_metrics", + "bidsv1.5.0": "desc-PCA_metrics" + }, + "ICA decomposition": { + "orig": "ica_decomposition", + "bidsv1.5.0": "desc-ICA_decomposition" + }, + "ICA metrics": { + "orig": "ica_metrics", + "bidsv1.5.0": "desc-tedana_metrics" + } +} diff --git a/tedana/config/tsv_table.json b/tedana/config/tsv_table.json new file mode 100644 index 000000000..ac77f75f5 --- /dev/null +++ b/tedana/config/tsv_table.json @@ -0,0 +1,30 @@ +{ + "PCA mixing": { + "orig": "pca_mixing", + "bidsv1.5.0": "desc-PCA_mixing" + }, + "PCA metrics": { + "orig": "pca_metrics", + "bidsv1.5.0": "desc-PCA_metrics" + }, + "ICA mixing": { + "orig": "ica_mixing", + "bidsv1.5.0": "desc-ICA_mixing" + }, + "ICA metrics": { + "orig": "ica_metrics", + "bidsv1.5.0": "desc-tedana_metrics" + }, + "global signal time series": { + "orig": "global_signal_ts", + "bidsv1.5.0": "desc-globalSignal_timeseries" + }, + "ICA MIR mixing": { + "orig": "ica_mir_mixing", + "bidsv1.5.0": "desc-ICAMIRDenoised_mixing" + }, + "ICA orthogonalized mixing": { + "orig": "ica_orth_mixing", + "bidsv1.5.0": "desc-ICAOrth_mixing" + } +} diff --git a/tedana/constants.py b/tedana/constants.py index 1a9accf68..95ef5b09e 100644 --- a/tedana/constants.py +++ b/tedana/constants.py @@ -33,201 +33,15 @@ occur. """ +from pathlib import Path +import os.path as op + allowed_conventions = ('orig', 'bidsv1.5.0') bids = 'bidsv1.5.0' -img_table = { - 'adaptive mask': { - 'orig': 'adaptive_mask', - 'bidsv1.5.0': 'desc-adaptiveGoodSignal_mask', - }, - 't2star map': { - 'orig': 't2sv', - 'bidsv1.5.0': 'T2starmap', - }, - 's0 map': { - 'orig': 's0v', - 'bidsv1.5.0': 'S0map', - }, - 'combined': { - 'orig': 'ts_OC', - 'bidsv1.5.0': 'desc-optcom_bold', - }, - 'ICA components': { - 'orig': 'ica_components', - 'bidsv1.5.0': 'desc-ICA_components', - }, - 'z-scored PCA components': { - 'orig': 'pca_components', - 'bidsv1.5.0': 'desc-PCA_stat-z_components', - }, - 'z-scored ICA components': { - 'orig': 'betas_OC', - 'bidsv1.5.0': 'desc-ICA_stat-z_components', - }, - 'ICA accepted components': { - 'orig': 'betas_hik_OC', - 'bidsv1.5.0': 'desc-ICAAccepted_components', - }, - 'z-scored ICA accepted components': { - 'orig': 'feats_OC2', - 'bidsv1.5.0': 'desc-ICAAccepted_stat-z_components', - }, - 'denoised ts': { - 'orig': 'dn_ts_OC', - 'bidsv1.5.0': 'desc-optcomDenoised_bold', - }, - 'high kappa ts': { - 'orig': 'hik_ts_OC', - 'bidsv1.5.0': 'desc-optcomAccepted_bold', - }, - 'low kappa ts': { - 'orig': 'lowk_ts_OC', - 'bidsv1.5.0': 'desc-optcomRejected_bold', - }, - # verbose outputs - 'full t2star map': { - 'orig': 't2svG', - 'bidsv1.5.0': 'desc-full_T2starmap', - }, - 'full s0 map': { - 'orig': 's0vG', - 'bidsv1.5.0': 'desc-full_S0map', - }, - 'whitened': { - 'orig': 'ts_OC_whitened', - 'bidsv1.5.0': 'desc-optcomPCAReduced_bold', - }, - 'echo weight PCA map split': { - 'orig': 'e{0}_PCA_comp', - 'bidsv1.5.0': 'echo-{0}_desc-PCA_components', - }, - 'echo R2 PCA split': { - 'orig': 'e{0}_PCA_R2', - 'bidsv1.5.0': 'echo-{0}_desc-PCAR2ModelPredictions_components', - }, - 'echo S0 PCA split': { - 'orig': 'e{0}_PCA_S0', - 'bidsv1.5.0': 'echo-{0}_desc-PCAS0ModelPredictions_components', - }, - 'PCA component weights': { - 'orig': 'pca_weights', - 'bidsv1.5.0': 'desc-PCAAveragingWeights_components', - }, - 'PCA reduced': { - 'orig': 'oc_reduced', - 'bidsv1.5.0': 'desc-optcomPCAReduced_bold', - }, - 'echo weight ICA map split': { - 'orig': 'e{0}_ICA_comp', - 'bidsv1.5.0': 'echo-{0}_desc-ICA_components', - }, - 'echo R2 ICA split': { - 'orig': 'e{0}_ICA_R2', - 'bidsv1.5.0': 'echo-{0}_desc-ICAR2ModelPredictions_components', - }, - 'echo S0 ICA split': { - 'orig': 'e{0}_ICA_S0', - 'bidsv1.5.0': 'echo-{0}_desc-ICAS0ModelPredictions_components', - }, - 'ICA component weights': { - 'orig': 'ica_weights', - 'bidsv1.5.0': 'desc-ICAAveragingWeights_components', - }, - 'high kappa ts split': { - 'orig': 'hik_ts_e{0}', - 'bidsv1.5.0': 'echo-{0}_desc-Accepted_bold', - }, - 'low kappa ts split': { - 'orig': 'lowk_ts_e{0}', - 'bidsv1.5.0': 'echo-{0}_desc-Rejected_bold', - }, - 'denoised ts split': { - 'orig': 'dn_ts_e{0}', - 'bidsv1.5.0': 'echo-{0}_desc-Denoised_bold', - }, - # global signal outputs - 'gs map': { - 'orig': 'T1gs', - 'bidsv1.5.0': 'desc-globalSignal_map', - }, - 'has gs combined': { - 'orig': 'tsoc_orig', - 'bidsv1.5.0': 'desc-optcomWithGlobalSignal_bold', - }, - 'removed gs combined': { - 'orig': 'tsoc_nogs', - 'bidsv1.5.0': 'desc-optcomNoGlobalSignal_bold', - }, - 't1 like': { - 'orig': 'sphis_hik', - 'bidsv1.5.0': 'desc-T1likeEffect_min', - }, - 'ICA accepted mir denoised': { - 'orig': 'hik_ts_OC_MIR', - 'bidsv1.5.0': 'desc-optcomAcceptedMIRDenoised_bold', - }, - 'mir denoised': { - 'orig': 'dn_ts_OC_MIR', - 'bidsv1.5.0': 'desc-optcomMIRDenoised_bold', - }, - 'ICA accepted mir component weights': { - 'orig': 'betas_hik_OC_MIR', - 'bidsv1.5.0': 'desc-ICAAcceptedMIRDenoised_components', - }, -} - -json_table = { - 'data description': { - 'orig': 'dataset_description', - 'bidsv1.5.0': 'dataset_description', - }, - 'PCA decomposition': { - 'orig': 'pca_decomposition', - 'bidsv1.5.0': 'desc-PCA_decomposition', - }, - 'PCA metrics': { - 'orig': 'pca_metrics', - 'bidsv1.5.0': 'desc-PCA_metrics', - }, - 'ICA decomposition': { - 'orig': 'ica_decomposition', - 'bidsv1.5.0': 'desc-ICA_decomposition', - }, - 'ICA metrics': { - 'orig': 'ica_metrics', - 'bidsv1.5.0': 'desc-tedana_metrics', - }, -} +config_path = op.join(Path(__file__).parent.absolute(), 'config') -tsv_table = { - 'PCA mixing': { - 'orig': 'pca_mixing', - 'bidsv1.5.0': 'desc-PCA_mixing', - }, - 'PCA metrics': { - 'orig': 'pca_metrics', - 'bidsv1.5.0': 'desc-PCA_metrics', - }, - 'ICA mixing': { - 'orig': 'ica_mixing', - 'bidsv1.5.0': 'desc-ICA_mixing', - }, - 'ICA metrics': { - 'orig': 'ica_metrics', - 'bidsv1.5.0': 'desc-tedana_metrics', - }, - 'global signal time series': { - 'orig': 'global_signal_ts', - 'bidsv1.5.0': 'desc-globalSignal_timeseries', - }, - 'ICA MIR mixing': { - 'orig': 'ica_mir_mixing', - 'bidsv1.5.0': 'desc-ICAMIRDenoised_mixing', - }, - 'ICA orthogonalized mixing': { - 'orig': 'ica_orth_mixing', - 'bidsv1.5.0': 'desc-ICAOrth_mixing', - }, -} +img_table_file = op.join(config_path, 'img_table.json') +json_table_file = op.join(config_path, 'json_table.json') +tsv_table_file = op.join(config_path, 'tsv_table.json') diff --git a/tedana/io.py b/tedana/io.py index 2c57c9253..af0414622 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -60,6 +60,7 @@ """ import logging import os.path as op +import json import numpy as np import nibabel as nib @@ -69,7 +70,8 @@ from tedana import utils from tedana.stats import computefeats2, get_coeffs from .constants import ( - bids, allowed_conventions, img_table, json_table, tsv_table + bids, allowed_conventions, + img_table_file, json_table_file, tsv_table_file ) @@ -85,6 +87,33 @@ convention = bids # overridden in API or CLI calls +def load_json(path: str) -> dict: + """Loads a json file from path + + Parameters + ---------- + path: str + The path to the json file to load + + Returns + ------- + A dict representation of the JSON data + + Raises + ------ + FileNotFoundError if the file does not exist + IsADirectoryError if the path is a directory instead of a file + """ + with open(path, 'r') as f: + data = json.load(f) + return data + + +img_table = load_json(img_table_file) +json_table = load_json(json_table_file) +tsv_table = load_json(tsv_table_file) + + # Naming Functions def set_convention(name: str) -> None: """Sets the convention for the io module @@ -138,7 +167,7 @@ def gen_img_name(img_type: str, echo: int = 0) -> str: See Also -------- - constants.img_table, a dict for translating various naming types + img_table, a dict for translating various naming types """ if echo: img_type += ' split' From c3aba1c8e41fddd9ffde40a1f3282e96e3e010f7 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Fri, 19 Mar 2021 16:50:35 -0400 Subject: [PATCH 30/39] Revert "Addresses some review concerns" This reverts commit 63903a4868e3a2eb895ebde47eb0d9af47981814. --- tedana/io.py | 25 +++++++++++++++++-------- tedana/workflows/t2smap.py | 8 +++++++- tedana/workflows/tedana.py | 8 +++++++- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index af0414622..fd872e8b5 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -14,12 +14,14 @@ Globals ------- outdir +prefix convention Naming Functions ---------------- set_convention +set_prefix gen_img_name gen_json_name gen_tsv_name @@ -79,11 +81,8 @@ RepLGR = logging.getLogger('REPORT') RefLGR = logging.getLogger('REFERENCES') -global outdir outdir = '.' -global prefix prefix = '' -global convention convention = bids # overridden in API or CLI calls @@ -123,10 +122,6 @@ def set_convention(name: str) -> None: name : {'orig', 'bidsv1.5.0', 'bids'} The convention name to set this module for - Returns - ------- - The valid string representation of a convention - Notes ----- Uses the `io.convention` module-wide variable @@ -135,7 +130,6 @@ def set_convention(name: str) -> None: ------ ValueError if the name is not valid """ - global convention if name in allowed_conventions: convention = name elif name == 'bids': @@ -145,6 +139,21 @@ def set_convention(name: str) -> None: LGR.info('Set convention as %s' % convention) +def set_prefix(pref: str) -> None: + """Sets the prefix for the io module + + Parameters + ---------- + pref : str + The prefix to set for the module. If the prefix is not blank, + filenames will have the prefix and underscore before all filenames + """ + if pref: + pref += '_' + prefix = pref + LGR.info('Set prefix as %s' % prefix) + + def gen_img_name(img_type: str, echo: int = 0) -> str: """Generates an image file full path to simplify file output diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index a09d37573..3ec9a2746 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -64,6 +64,11 @@ def _get_parser(): 'Dependent ANAlysis. Must be in the same ' 'space as `data`.'), default=None) + optional.add_argument('--prefix', + dest='prefix', + type=str, + help='Prefix for filenames generated.', + default='') optional.add_argument('--convention', dest='convention', action='store', @@ -124,7 +129,7 @@ def _get_parser(): def t2smap_workflow(data, tes, out_dir='.', mask=None, - convention='bids', + prefix='', convention='bids', fittype='loglin', fitmode='all', combmode='t2s', debug=False, quiet=False): """ @@ -187,6 +192,7 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, if not op.isdir(out_dir): os.mkdir(out_dir) io.outdir = out_dir + io.set_prefix(prefix) io.set_convention(convention) if debug and not quiet: diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 08ebe3c47..ac3e47d6b 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -78,6 +78,11 @@ def _get_parser(): "function will be used to derive a mask " "from the first echo's data."), default=None) + optional.add_argument('--prefix', + dest='prefix', + type=str, + help="Prefix for filenames generated.", + default='') optional.add_argument('--convention', dest='convention', action='store', @@ -243,7 +248,7 @@ def _get_parser(): def tedana_workflow(data, tes, out_dir='.', mask=None, - convention='bids', + convention='bids', prefix='', fittype='loglin', combmode='t2s', tedpca='mdl', fixed_seed=42, maxit=500, maxrestart=10, tedort=False, gscontrol=None, @@ -340,6 +345,7 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, os.mkdir(out_dir) io.outdir = out_dir + io.set_prefix(prefix) io.set_convention(convention) # boilerplate From b2a4b093c5b79cdf826f420473fb875c4fd2e673 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 22 Mar 2021 13:35:07 -0400 Subject: [PATCH 31/39] Fix global issue --- tedana/io.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tedana/io.py b/tedana/io.py index fd872e8b5..b947e7daf 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -130,6 +130,7 @@ def set_convention(name: str) -> None: ------ ValueError if the name is not valid """ + global convention if name in allowed_conventions: convention = name elif name == 'bids': @@ -148,6 +149,7 @@ def set_prefix(pref: str) -> None: The prefix to set for the module. If the prefix is not blank, filenames will have the prefix and underscore before all filenames """ + global prefix if pref: pref += '_' prefix = pref From dc1bb631702f722d1705bc2b325bcdc84a1ac616 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 22 Mar 2021 14:15:04 -0400 Subject: [PATCH 32/39] Work around 3.5 --- tedana/constants.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tedana/constants.py b/tedana/constants.py index 95ef5b09e..f37d04b57 100644 --- a/tedana/constants.py +++ b/tedana/constants.py @@ -42,6 +42,6 @@ config_path = op.join(Path(__file__).parent.absolute(), 'config') -img_table_file = op.join(config_path, 'img_table.json') -json_table_file = op.join(config_path, 'json_table.json') -tsv_table_file = op.join(config_path, 'tsv_table.json') +img_table_file = op.join(str(config_path), 'img_table.json') +json_table_file = op.join(str(config_path), 'json_table.json') +tsv_table_file = op.join(str(config_path), 'tsv_table.json') From 017c8614a4227b3a5232aa4152dc1d6f293f450f Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 22 Mar 2021 14:53:01 -0400 Subject: [PATCH 33/39] Actually fix the string conversion --- tedana/constants.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tedana/constants.py b/tedana/constants.py index f37d04b57..29ff750c5 100644 --- a/tedana/constants.py +++ b/tedana/constants.py @@ -40,8 +40,8 @@ bids = 'bidsv1.5.0' -config_path = op.join(Path(__file__).parent.absolute(), 'config') +config_path = op.join(str(Path(__file__).parent.absolute()), 'config') -img_table_file = op.join(str(config_path), 'img_table.json') -json_table_file = op.join(str(config_path), 'json_table.json') -tsv_table_file = op.join(str(config_path), 'tsv_table.json') +img_table_file = op.join(config_path, 'img_table.json') +json_table_file = op.join(config_path, 'json_table.json') +tsv_table_file = op.join(config_path, 'tsv_table.json') From 84aa513ae512f8e852de407a6f25223d38687d2c Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 22 Mar 2021 19:22:33 -0400 Subject: [PATCH 34/39] Fixes missing desc- --- tedana/config/img_table.json | 4 ++-- tedana/tests/data/cornell_three_echo_outputs.txt | 4 ++-- tedana/tests/data/fiu_four_echo_outputs.txt | 4 ++-- tedana/tests/data/nih_five_echo_outputs_t2smap.txt | 4 ++-- tedana/tests/data/nih_five_echo_outputs_verbose.txt | 4 ++-- tedana/workflows/t2smap.py | 2 +- tedana/workflows/tedana.py | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tedana/config/img_table.json b/tedana/config/img_table.json index 8dc27568e..206cc27f9 100644 --- a/tedana/config/img_table.json +++ b/tedana/config/img_table.json @@ -5,11 +5,11 @@ }, "t2star map": { "orig": "t2sv", - "bidsv1.5.0": "T2starmap" + "bidsv1.5.0": "desc-T2starmap" }, "s0 map": { "orig": "s0v", - "bidsv1.5.0": "S0map" + "bidsv1.5.0": "desc-S0map" }, "combined": { "orig": "ts_OC", diff --git a/tedana/tests/data/cornell_three_echo_outputs.txt b/tedana/tests/data/cornell_three_echo_outputs.txt index 62dabfdf0..fa05541db 100644 --- a/tedana/tests/data/cornell_three_echo_outputs.txt +++ b/tedana/tests/data/cornell_three_echo_outputs.txt @@ -1,5 +1,5 @@ -S0map.nii.gz -T2starmap.nii.gz +desc-S0map.nii.gz +desc-T2starmap.nii.gz dataset_description.json desc-ICAAccepted_components.nii.gz desc-ICAAccepted_stat-z_components.nii.gz diff --git a/tedana/tests/data/fiu_four_echo_outputs.txt b/tedana/tests/data/fiu_four_echo_outputs.txt index 9c1e4b3bc..7643a6d61 100644 --- a/tedana/tests/data/fiu_four_echo_outputs.txt +++ b/tedana/tests/data/fiu_four_echo_outputs.txt @@ -1,5 +1,5 @@ -S0map.nii.gz -T2starmap.nii.gz +desc-S0map.nii.gz +desc-T2starmap.nii.gz dataset_description.json desc-ICAAcceptedMIRDenoised_components.nii.gz desc-ICAAccepted_components.nii.gz diff --git a/tedana/tests/data/nih_five_echo_outputs_t2smap.txt b/tedana/tests/data/nih_five_echo_outputs_t2smap.txt index 44424c03f..fe2007725 100644 --- a/tedana/tests/data/nih_five_echo_outputs_t2smap.txt +++ b/tedana/tests/data/nih_five_echo_outputs_t2smap.txt @@ -2,5 +2,5 @@ dataset_description.json desc-full_S0map.nii.gz desc-full_T2starmap.nii.gz desc-optcom_bold.nii.gz -S0map.nii.gz -T2starmap.nii.gz +desc-S0map.nii.gz +desc-T2starmap.nii.gz diff --git a/tedana/tests/data/nih_five_echo_outputs_verbose.txt b/tedana/tests/data/nih_five_echo_outputs_verbose.txt index 5de724477..fcf8faa20 100644 --- a/tedana/tests/data/nih_five_echo_outputs_verbose.txt +++ b/tedana/tests/data/nih_five_echo_outputs_verbose.txt @@ -1,5 +1,5 @@ -S0map.nii.gz -T2starmap.nii.gz +desc-S0map.nii.gz +desc-T2starmap.nii.gz dataset_description.json desc-ICAAccepted_components.nii.gz desc-ICAAccepted_stat-z_components.nii.gz diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index 3ec9a2746..8117d8b06 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -175,7 +175,7 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, ========================== ================================================= Filename Content ========================== ================================================= - T2starmap.nii.gz Limited estimated T2* 3D map or 4D timeseries. + desc-T2starmap.nii.gz Limited estimated T2* 3D map or 4D timeseries. Will be a 3D map if ``fitmode`` is 'all' and a 4D timeseries if it is 'ts'. S0map.nii.gz Limited S0 3D map or 4D timeseries. diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index ac3e47d6b..9b2578a55 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -460,12 +460,12 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, # coerce to list of integers manacc = [int(m) for m in manacc] + t2smap_file = io.gen_img_name('t2star map') if t2smap is not None and op.isfile(t2smap): t2smap = op.abspath(t2smap) # Allow users to re-run on same folder - if t2smap != op.join(out_dir, 'T2starmap.nii.gz'): - shutil.copyfile(t2smap, op.join(out_dir, 'T2starmap.nii.gz')) - shutil.copyfile(t2smap, op.join(out_dir, op.basename(t2smap))) + if t2smap != t2smap_file: + shutil.copyfile(t2smap, t2smap_file) elif t2smap is not None: raise IOError('Argument "t2smap" must be an existing file.') From 6b6cdff8cc127bc7489ce5c9483799ea22130647 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 22 Mar 2021 19:33:51 -0400 Subject: [PATCH 35/39] Revert "Fixes missing desc-" This reverts commit 84aa513ae512f8e852de407a6f25223d38687d2c. --- tedana/config/img_table.json | 4 ++-- tedana/tests/data/cornell_three_echo_outputs.txt | 4 ++-- tedana/tests/data/fiu_four_echo_outputs.txt | 4 ++-- tedana/tests/data/nih_five_echo_outputs_t2smap.txt | 4 ++-- tedana/tests/data/nih_five_echo_outputs_verbose.txt | 4 ++-- tedana/workflows/t2smap.py | 2 +- tedana/workflows/tedana.py | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tedana/config/img_table.json b/tedana/config/img_table.json index 206cc27f9..8dc27568e 100644 --- a/tedana/config/img_table.json +++ b/tedana/config/img_table.json @@ -5,11 +5,11 @@ }, "t2star map": { "orig": "t2sv", - "bidsv1.5.0": "desc-T2starmap" + "bidsv1.5.0": "T2starmap" }, "s0 map": { "orig": "s0v", - "bidsv1.5.0": "desc-S0map" + "bidsv1.5.0": "S0map" }, "combined": { "orig": "ts_OC", diff --git a/tedana/tests/data/cornell_three_echo_outputs.txt b/tedana/tests/data/cornell_three_echo_outputs.txt index fa05541db..62dabfdf0 100644 --- a/tedana/tests/data/cornell_three_echo_outputs.txt +++ b/tedana/tests/data/cornell_three_echo_outputs.txt @@ -1,5 +1,5 @@ -desc-S0map.nii.gz -desc-T2starmap.nii.gz +S0map.nii.gz +T2starmap.nii.gz dataset_description.json desc-ICAAccepted_components.nii.gz desc-ICAAccepted_stat-z_components.nii.gz diff --git a/tedana/tests/data/fiu_four_echo_outputs.txt b/tedana/tests/data/fiu_four_echo_outputs.txt index 7643a6d61..9c1e4b3bc 100644 --- a/tedana/tests/data/fiu_four_echo_outputs.txt +++ b/tedana/tests/data/fiu_four_echo_outputs.txt @@ -1,5 +1,5 @@ -desc-S0map.nii.gz -desc-T2starmap.nii.gz +S0map.nii.gz +T2starmap.nii.gz dataset_description.json desc-ICAAcceptedMIRDenoised_components.nii.gz desc-ICAAccepted_components.nii.gz diff --git a/tedana/tests/data/nih_five_echo_outputs_t2smap.txt b/tedana/tests/data/nih_five_echo_outputs_t2smap.txt index fe2007725..44424c03f 100644 --- a/tedana/tests/data/nih_five_echo_outputs_t2smap.txt +++ b/tedana/tests/data/nih_five_echo_outputs_t2smap.txt @@ -2,5 +2,5 @@ dataset_description.json desc-full_S0map.nii.gz desc-full_T2starmap.nii.gz desc-optcom_bold.nii.gz -desc-S0map.nii.gz -desc-T2starmap.nii.gz +S0map.nii.gz +T2starmap.nii.gz diff --git a/tedana/tests/data/nih_five_echo_outputs_verbose.txt b/tedana/tests/data/nih_five_echo_outputs_verbose.txt index fcf8faa20..5de724477 100644 --- a/tedana/tests/data/nih_five_echo_outputs_verbose.txt +++ b/tedana/tests/data/nih_five_echo_outputs_verbose.txt @@ -1,5 +1,5 @@ -desc-S0map.nii.gz -desc-T2starmap.nii.gz +S0map.nii.gz +T2starmap.nii.gz dataset_description.json desc-ICAAccepted_components.nii.gz desc-ICAAccepted_stat-z_components.nii.gz diff --git a/tedana/workflows/t2smap.py b/tedana/workflows/t2smap.py index 8117d8b06..3ec9a2746 100644 --- a/tedana/workflows/t2smap.py +++ b/tedana/workflows/t2smap.py @@ -175,7 +175,7 @@ def t2smap_workflow(data, tes, out_dir='.', mask=None, ========================== ================================================= Filename Content ========================== ================================================= - desc-T2starmap.nii.gz Limited estimated T2* 3D map or 4D timeseries. + T2starmap.nii.gz Limited estimated T2* 3D map or 4D timeseries. Will be a 3D map if ``fitmode`` is 'all' and a 4D timeseries if it is 'ts'. S0map.nii.gz Limited S0 3D map or 4D timeseries. diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index 9b2578a55..ac3e47d6b 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -460,12 +460,12 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, # coerce to list of integers manacc = [int(m) for m in manacc] - t2smap_file = io.gen_img_name('t2star map') if t2smap is not None and op.isfile(t2smap): t2smap = op.abspath(t2smap) # Allow users to re-run on same folder - if t2smap != t2smap_file: - shutil.copyfile(t2smap, t2smap_file) + if t2smap != op.join(out_dir, 'T2starmap.nii.gz'): + shutil.copyfile(t2smap, op.join(out_dir, 'T2starmap.nii.gz')) + shutil.copyfile(t2smap, op.join(out_dir, op.basename(t2smap))) elif t2smap is not None: raise IOError('Argument "t2smap" must be an existing file.') From 9ba0ec9511c90cb07cf4d724d6d360c162631902 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Mon, 22 Mar 2021 19:37:19 -0400 Subject: [PATCH 36/39] Replace hard-coded T2starmap with io call --- tedana/workflows/tedana.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index ac3e47d6b..9a0bf8da3 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -461,11 +461,11 @@ def tedana_workflow(data, tes, out_dir='.', mask=None, manacc = [int(m) for m in manacc] if t2smap is not None and op.isfile(t2smap): + t2smap_file = io.gen_img_name('t2star map') t2smap = op.abspath(t2smap) # Allow users to re-run on same folder - if t2smap != op.join(out_dir, 'T2starmap.nii.gz'): - shutil.copyfile(t2smap, op.join(out_dir, 'T2starmap.nii.gz')) - shutil.copyfile(t2smap, op.join(out_dir, op.basename(t2smap))) + if t2smap != io.gen_img_name('t2star map'): + shutil.copyfile(t2smap, t2smap_file) elif t2smap is not None: raise IOError('Argument "t2smap" must be an existing file.') From 0c87c74a89b2076017829ce6ad949ded56c357f0 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 23 Mar 2021 08:20:10 -0400 Subject: [PATCH 37/39] Fixes docstring for io.py --- tedana/io.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tedana/io.py b/tedana/io.py index b947e7daf..de36bf0ee 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -502,8 +502,8 @@ def filewrite(data, img_type, ref_img, gzip=True, copy_header=True, ---------- data : (S [x T]) array_like Data to be saved - filename : :obj:`str` - Filepath where data should be saved to + img_type : :obj: `str` + The type of file to write ref_img : :obj:`str` or img_like Reference image gzip : :obj:`bool`, optional From 9d1592640045540e5da58d355efed625a20463ec Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 23 Mar 2021 14:22:33 -0400 Subject: [PATCH 38/39] Fix space --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index de36bf0ee..8626719a9 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -502,7 +502,7 @@ def filewrite(data, img_type, ref_img, gzip=True, copy_header=True, ---------- data : (S [x T]) array_like Data to be saved - img_type : :obj: `str` + img_type : :obj:`str` The type of file to write ref_img : :obj:`str` or img_like Reference image From 1b61d7fcb21f99a0a9074fd38301c50fd06f2e42 Mon Sep 17 00:00:00 2001 From: Joshua Teves Date: Tue, 23 Mar 2021 14:22:59 -0400 Subject: [PATCH 39/39] Fix space --- tedana/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tedana/io.py b/tedana/io.py index 8626719a9..c220cbf3c 100644 --- a/tedana/io.py +++ b/tedana/io.py @@ -511,7 +511,7 @@ def filewrite(data, img_type, ref_img, gzip=True, copy_header=True, if output dtype is NIFTI. Default: True copy_header : :obj:`bool`, optional Whether to copy header from `ref_img` to new image. Default: True - echo : :obj: `int`, optional + echo : :obj:`int`, optional Indicate the echo index of the data being written. Returns