def split_ts(data, mmix, mask, acc): """ 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 acc : :obj:`list` List of accepted components used to subset `mmix` Returns ------- hikts : (S x T) :obj:`numpy.ndarray` Time series reconstructed using only components in `acc` rest : (S x T) :obj:`numpy.ndarray` Original data with `hikts` removed """ cbetas = model.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 split_ts(data, mmix, mask, acc): """ 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 acc : list List of accepted components used to subset `mmix` Returns ------- hikts : (S x T) :obj:`numpy.ndarray` Time series reconstructed using only components in `acc` rest : (S x T) :obj:`numpy.ndarray` Original data with `hikts` removed """ cbetas = model.get_coeffs(data - data.mean(axis=-1, keepdims=True), mask, mmix) betas = cbetas[mask] if len(acc) != 0: hikts = utils.unmask(betas[:, acc].dot(mmix.T[acc, :]), mask) else: hikts = None return hikts, data - hikts
def write_split_ts(data, mmix, mask, acc, rej, midk, ref_img, suffix=''): """ Splits `data` into denoised / noise / ignored time series and saves to disk Parameters ---------- data : (S x T) array_like Input time series mmix : (C x T) 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 acc : list Indices of accepted (BOLD) components in `mmix` rej : list Indices of rejected (non-BOLD) components in `mmix` midk : list Indices of mid-K (questionable) components in `mmix` ref_img : str or img_like Reference image to dictate how outputs are saved to disk suffix : str, optional Appended to name of saved files (before extension). Default: '' Returns ------- varexpl : float Percent variance of data explained by extracted + retained components """ # mask and de-mean data mdata = data[mask] dmdata = mdata.T - mdata.T.mean(axis=0) # get variance explained by retained components betas = model.get_coeffs(utils.unmask(dmdata.T, mask), mask, mmix)[mask] varexpl = (1 - ((dmdata.T - betas.dot(mmix.T))**2.).sum() / (dmdata**2.).sum()) * 100 LGR.info('Variance explained by ICA decomposition: {:.02f}%'.format(varexpl)) # create component and de-noised time series and save to files hikts = betas[:, acc].dot(mmix.T[acc, :]) midkts = betas[:, midk].dot(mmix.T[midk, :]) lowkts = betas[:, rej].dot(mmix.T[rej, :]) dnts = data[mask] - lowkts - midkts if len(acc) != 0: fout = utils.filewrite(utils.unmask(hikts, mask), 'hik_ts_{0}'.format(suffix), ref_img) LGR.info('Writing high-Kappa time series: {}'.format(op.abspath(fout))) if len(midk) != 0: fout = utils.filewrite(utils.unmask(midkts, mask), 'midk_ts_{0}'.format(suffix), ref_img) LGR.info('Writing mid-Kappa time series: {}'.format(op.abspath(fout))) if len(rej) != 0: fout = utils.filewrite(utils.unmask(lowkts, mask), 'lowk_ts_{0}'.format(suffix), ref_img) LGR.info('Writing low-Kappa time series: {}'.format(op.abspath(fout))) fout = utils.filewrite(utils.unmask(dnts, mask), 'dn_ts_{0}'.format(suffix), ref_img) LGR.info('Writing denoised time series: {}'.format(op.abspath(fout))) return varexpl
def writeresults(ts, mask, comptable, mmix, n_vols, acc, rej, midk, empty, ref_img): """ Denoises `ts` and saves all resulting files to disk Parameters ---------- ts : (S x T) array_like Time series to denoise and save to disk mask : (S,) array_like Boolean mask array comptable : (N x 5) array_like Array with columns denoting (1) index of component, (2) Kappa score of component, (3) Rho score of component, (4) variance explained by component, and (5) normalized variance explained by component mmix : (C x T) array_like Mixing matrix for converting input data to component space, where `C` is components and `T` is the same as in `data` acc : list Indices of accepted (BOLD) components in `mmix` rej : list Indices of rejected (non-BOLD) components in `mmix` midk : list Indices of mid-K (questionable) components in `mmix` empty : list Indices of ignored components in `mmix` ref_img : str or img_like Reference image to dictate how outputs are saved to disk """ fout = utils.filewrite(ts, 'ts_OC', ref_img) LGR.info('Writing optimally-combined time series: {}'.format(op.abspath(fout))) varexpl = write_split_ts(ts, mmix, mask, acc, rej, midk, ref_img, suffix='OC') ts_B = model.get_coeffs(ts, mask, mmix) fout = utils.filewrite(ts_B, 'betas_OC', ref_img) LGR.info('Writing full ICA coefficient feature set: {}'.format(op.abspath(fout))) if len(acc) != 0: fout = utils.filewrite(ts_B[:, acc], 'betas_hik_OC', ref_img) LGR.info('Writing denoised ICA coefficient feature set: {}'.format(op.abspath(fout))) fout = writefeats(split_ts(ts, mmix, mask, acc)[0], mmix[:, acc], mask, ref_img, suffix='OC2') LGR.info('Writing Z-normalized spatial component maps: {}'.format(op.abspath(fout))) writect(comptable, n_vols, acc, rej, midk, empty, ctname='comp_table.txt', varexpl=varexpl) LGR.info('Writing component table: {}'.format(op.abspath('comp_table.txt')))
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` rest : (S x T) :obj:`numpy.ndarray` Original data with `hikts` removed """ acc = comptable[comptable.classification == 'accepted'].index.values cbetas = model.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 writeresults(ts, mask, comptable, mmix, n_vols, fixed_seed, acc, rej, midk, empty, ref_img): """ Denoises `ts` and saves all resulting files to disk Parameters ---------- ts : (S x T) array_like Time series to denoise and save to disk mask : (S,) array_like Boolean mask array comptable : (N x 5) array_like Array with columns denoting (1) index of component, (2) Kappa score of component, (3) Rho score of component, (4) variance explained by component, and (5) normalized variance explained by component mmix : (C x T) array_like Mixing matrix for converting input data to component space, where `C` is components and `T` is the same as in `data` n_vols : :obj:`int` Number of volumes in original time series fixed_seed: :obj:`int` Integer value used in seeding ICA acc : :obj:`list` Indices of accepted (BOLD) components in `mmix` rej : :obj:`list` Indices of rejected (non-BOLD) components in `mmix` midk : :obj:`list` Indices of mid-K (questionable) components in `mmix` empty : :obj:`list` Indices of ignored components in `mmix` ref_img : :obj:`str` or img_like Reference image to dictate how outputs are saved to disk Notes ----- This function writes out several files: ====================== ================================================= Filename Content ====================== ================================================= ts_OC.nii Optimally combined 4D time series. hik_ts_OC.nii High-Kappa time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. midk_ts_OC.nii Mid-Kappa time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. low_ts_OC.nii Low-Kappa time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. dn_ts_OC.nii Denoised time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. betas_OC.nii Full ICA coefficient feature set. betas_hik_OC.nii Denoised ICA coefficient feature set. feats_OC2.nii Z-normalized spatial component maps. Generated by :py:func:`tedana.utils.io.writefeats`. comp_table.txt Component table. Generated by :py:func:`tedana.utils.io.writect`. ====================== ================================================= """ fout = filewrite(ts, 'ts_OC', ref_img) LGR.info('Writing optimally-combined time series: {}'.format(op.abspath(fout))) write_split_ts(ts, mmix, mask, acc, rej, midk, ref_img, suffix='OC') ts_B = model.get_coeffs(ts, mmix, mask) fout = filewrite(ts_B, 'betas_OC', ref_img) LGR.info('Writing full ICA coefficient feature set: {}'.format(op.abspath(fout))) if len(acc) != 0: fout = filewrite(ts_B[:, acc], 'betas_hik_OC', ref_img) LGR.info('Writing denoised ICA coefficient feature set: {}'.format(op.abspath(fout))) fout = writefeats(split_ts(ts, mmix, mask, acc)[0], mmix[:, acc], mask, ref_img, suffix='OC2') LGR.info('Writing Z-normalized spatial component maps: {}'.format(op.abspath(fout)))
def write_split_ts(data, mmix, mask, acc, rej, midk, ref_img, suffix=''): """ Splits `data` into denoised / noise / ignored time series and saves to disk Parameters ---------- data : (S x T) array_like Input time series mmix : (C x T) 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 acc : :obj:`list` Indices of accepted (BOLD) components in `mmix` rej : :obj:`list` Indices of rejected (non-BOLD) components in `mmix` midk : :obj:`list` Indices of mid-K (questionable) components in `mmix` ref_img : :obj:`str` or img_like Reference image to dictate how outputs are saved to disk suffix : :obj:`str`, optional Appended to name of saved files (before extension). Default: '' Returns ------- varexpl : :obj:`float` Percent variance of data explained by extracted + retained components Notes ----- This function writes out several files: ====================== ================================================= Filename Content ====================== ================================================= hik_ts_[suffix].nii High-Kappa time series. midk_ts_[suffix].nii Mid-Kappa time series. low_ts_[suffix].nii Low-Kappa time series. dn_ts_[suffix].nii Denoised time series. ====================== ================================================= """ # mask and de-mean data mdata = data[mask] dmdata = mdata.T - mdata.T.mean(axis=0) # get variance explained by retained components betas = model.get_coeffs(dmdata.T, mmix, mask=None) varexpl = (1 - ((dmdata.T - betas.dot(mmix.T))**2.).sum() / (dmdata**2.).sum()) * 100 LGR.info('Variance explained by ICA decomposition: ' '{:.02f}%'.format(varexpl)) # create component and de-noised time series and save to files hikts = betas[:, acc].dot(mmix.T[acc, :]) midkts = betas[:, midk].dot(mmix.T[midk, :]) lowkts = betas[:, rej].dot(mmix.T[rej, :]) dnts = data[mask] - lowkts - midkts if len(acc) != 0: fout = filewrite(utils.unmask(hikts, mask), 'hik_ts_{0}'.format(suffix), ref_img) LGR.info('Writing high-Kappa time series: {}'.format(op.abspath(fout))) if len(midk) != 0: fout = filewrite(utils.unmask(midkts, mask), 'midk_ts_{0}'.format(suffix), ref_img) LGR.info('Writing mid-Kappa time series: {}'.format(op.abspath(fout))) if len(rej) != 0: fout = filewrite(utils.unmask(lowkts, mask), 'lowk_ts_{0}'.format(suffix), ref_img) LGR.info('Writing low-Kappa time series: {}'.format(op.abspath(fout))) fout = filewrite(utils.unmask(dnts, mask), 'dn_ts_{0}'.format(suffix), ref_img) LGR.info('Writing denoised time series: {}'.format(op.abspath(fout))) return varexpl
def writeresults(ts, mask, comptable, mmix, n_vols, acc, rej, midk, empty, ref_img): """ Denoises `ts` and saves all resulting files to disk Parameters ---------- ts : (S x T) array_like Time series to denoise and save to disk mask : (S,) array_like Boolean mask array comptable : (N x 5) array_like Array with columns denoting (1) index of component, (2) Kappa score of component, (3) Rho score of component, (4) variance explained by component, and (5) normalized variance explained by component mmix : (C x T) array_like Mixing matrix for converting input data to component space, where `C` is components and `T` is the same as in `data` acc : list Indices of accepted (BOLD) components in `mmix` rej : list Indices of rejected (non-BOLD) components in `mmix` midk : list Indices of mid-K (questionable) components in `mmix` empty : list Indices of ignored components in `mmix` ref_img : str or img_like Reference image to dictate how outputs are saved to disk """ fout = utils.filewrite(ts, 'ts_OC', ref_img) LGR.info('Writing optimally-combined time series: {}'.format( op.abspath(fout))) varexpl = write_split_ts(ts, mmix, mask, acc, rej, midk, ref_img, suffix='OC') ts_B = model.get_coeffs(ts, mask, mmix) fout = utils.filewrite(ts_B, 'betas_OC', ref_img) LGR.info('Writing full ICA coefficient feature set: {}'.format( op.abspath(fout))) if len(acc) != 0: fout = utils.filewrite(ts_B[:, acc], 'betas_hik_OC', ref_img) LGR.info('Writing denoised ICA coefficient feature set: {}'.format( op.abspath(fout))) fout = writefeats(split_ts(ts, mmix, mask, acc)[0], mmix[:, acc], mask, ref_img, suffix='OC2') LGR.info('Writing Z-normalized spatial component maps: {}'.format( op.abspath(fout))) writect(comptable, n_vols, acc, rej, midk, empty, ctname='comp_table.txt', varexpl=varexpl) LGR.info('Writing component table: {}'.format( op.abspath('comp_table.txt')))
def write_comp_figs(ts, mask, comptable, mmix, ref_img, out_dir, png_cmap): """ Creates static figures that highlight certain aspects of tedana processing This includes a figure for each component showing the component time course, the spatial weight map and a fast Fourier transform of the time course Parameters ---------- ts : (S x T) array_like Time series from which to derive ICA betas 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. The index should be the component number. mmix : (C x T) array_like Mixing matrix for converting input data to component space, where `C` is components and `T` is the same as in `data` ref_img : :obj:`str` or img_like Reference image to dictate how outputs are saved to disk out_dir : :obj:`str` Figures folder within output directory png_cmap : :obj:`str` The name of a matplotlib colormap to use when making figures. Optional. Default colormap is 'coolwarm' """ # Get the lenght of the timeseries n_vols = len(mmix) # Check that colormap provided exists if png_cmap not in plt.colormaps(): LGR.warning( 'Provided colormap is not recognized, proceeding with default') png_cmap = 'coolwarm' # regenerate the beta images ts_B = model.get_coeffs(ts, mmix, mask) ts_B = ts_B.reshape(ref_img.shape[:3] + ts_B.shape[1:]) # trim edges from ts_B array ts_B = trim_edge_zeros(ts_B) # Mask out remaining zeros ts_B = np.ma.masked_where(ts_B == 0, ts_B) # Get repetition time from ref_img tr = ref_img.header.get_zooms()[-1] # Create indices for 6 cuts, based on dimensions cuts = [ts_B.shape[dim] // 6 for dim in range(3)] expl_text = '' # Remove trailing ';' from rationale column comptable['rationale'] = comptable['rationale'].str.rstrip(';') for compnum in comptable.index.values: if comptable.loc[compnum, "classification"] == 'accepted': line_color = 'g' expl_text = 'accepted' elif comptable.loc[compnum, "classification"] == 'rejected': line_color = 'r' expl_text = 'rejection reason(s): ' + comptable.loc[compnum, "rationale"] elif comptable.loc[compnum, "classification"] == 'ignored': line_color = 'k' expl_text = 'ignored reason(s): ' + comptable.loc[compnum, "rationale"] else: # Classification not added # If new, this will keep code running line_color = '0.75' expl_text = 'other classification' allplot = plt.figure(figsize=(10, 9)) ax_ts = plt.subplot2grid((5, 6), (0, 0), rowspan=1, colspan=6, fig=allplot) ax_ts.set_xlabel('TRs') ax_ts.set_xlim(0, n_vols) plt.yticks([]) # Make a second axis with units of time (s) max_xticks = 10 xloc = plt.MaxNLocator(max_xticks) ax_ts.xaxis.set_major_locator(xloc) ax_ts2 = ax_ts.twiny() ax1Xs = ax_ts.get_xticks() ax2Xs = [] for X in ax1Xs: # Limit to 2 decimal places seconds_val = round(X * tr, 2) ax2Xs.append(seconds_val) ax_ts2.set_xticks(ax1Xs) ax_ts2.set_xlim(ax_ts.get_xbound()) ax_ts2.set_xticklabels(ax2Xs) ax_ts2.set_xlabel('seconds') ax_ts.plot(mmix[:, compnum], color=line_color) # Title will include variance from comptable comp_var = "{0:.2f}".format(comptable.loc[compnum, "variance explained"]) comp_kappa = "{0:.2f}".format(comptable.loc[compnum, "kappa"]) comp_rho = "{0:.2f}".format(comptable.loc[compnum, "rho"]) plt_title = ('Comp. {}: variance: {}%, kappa: {}, rho: {}, ' '{}'.format(compnum, comp_var, comp_kappa, comp_rho, expl_text)) title = ax_ts.set_title(plt_title) title.set_y(1.5) # Set range to ~1/10th of max positive or negative beta imgmax = 0.1 * np.abs(ts_B[:, :, :, compnum]).max() imgmin = imgmax * -1 for idx, cut in enumerate(cuts): for imgslice in range(1, 6): ax = plt.subplot2grid((5, 6), (idx + 1, imgslice - 1), rowspan=1, colspan=1) ax.axis('off') if idx == 0: to_plot = np.rot90(ts_B[imgslice * cuts[idx], :, :, compnum]) if idx == 1: to_plot = np.rot90(ts_B[:, imgslice * cuts[idx], :, compnum]) if idx == 2: to_plot = ts_B[:, :, imgslice * cuts[idx], compnum] ax_im = ax.imshow(to_plot, vmin=imgmin, vmax=imgmax, aspect='equal', cmap=png_cmap) # Add a color bar to the plot. ax_cbar = allplot.add_axes([0.8, 0.3, 0.03, 0.37]) cbar = allplot.colorbar(ax_im, ax_cbar) cbar.set_label('Component Beta', rotation=90) cbar.ax.yaxis.set_label_position('left') # Get fft and freqs for this subject # adapted from @dangom spectrum, freqs = get_spectrum(mmix[:, compnum], tr) # Plot it ax_fft = plt.subplot2grid((5, 6), (4, 0), rowspan=1, colspan=6) ax_fft.plot(freqs, spectrum) ax_fft.set_title('One Sided fft') ax_fft.set_xlabel('Hz') ax_fft.set_xlim(freqs[0], freqs[-1]) plt.yticks([]) # Fix spacing so TR label does overlap with other plots allplot.subplots_adjust(hspace=0.4) plot_name = 'comp_{}.png'.format(str(compnum).zfill(3)) compplot_name = os.path.join(out_dir, plot_name) plt.savefig(compplot_name) plt.close()
def writeresults(ts, mask, comptable, mmix, n_vols, ref_img): """ Denoises `ts` and saves all resulting files to disk Parameters ---------- ts : (S x T) array_like Time series to denoise and save to disk 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". mmix : (C x T) array_like Mixing matrix for converting input data to component space, where `C` is components and `T` is the same as in `data` n_vols : :obj:`int` Number of volumes in original time series ref_img : :obj:`str` or img_like Reference image to dictate how outputs are saved to disk Notes ----- This function writes out several files: ====================== ================================================= Filename Content ====================== ================================================= ts_OC.nii Optimally combined 4D time series. hik_ts_OC.nii High-Kappa time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. midk_ts_OC.nii Mid-Kappa time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. low_ts_OC.nii Low-Kappa time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. dn_ts_OC.nii Denoised time series. Generated by :py:func:`tedana.utils.io.write_split_ts`. betas_OC.nii Full ICA coefficient feature set. betas_hik_OC.nii Denoised ICA coefficient feature set. feats_OC2.nii Z-normalized spatial component maps. Generated by :py:func:`tedana.utils.io.writefeats`. comp_table.txt Component table. Generated by :py:func:`tedana.utils.io.writect`. ====================== ================================================= """ acc = comptable[comptable.classification == 'accepted'].index.values fout = filewrite(ts, 'ts_OC', ref_img) LGR.info('Writing optimally-combined time series: {}'.format(op.abspath(fout))) write_split_ts(ts, mmix, mask, comptable, ref_img, suffix='OC') ts_B = model.get_coeffs(ts, mmix, mask) fout = filewrite(ts_B, 'betas_OC', ref_img) LGR.info('Writing full ICA coefficient feature set: {}'.format(op.abspath(fout))) if len(acc) != 0: fout = filewrite(ts_B[:, acc], 'betas_hik_OC', 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, suffix='OC2') LGR.info('Writing Z-normalized spatial component maps: {}'.format(op.abspath(fout)))