def plot_contrast_matrix(contrast_def, design_matrix, colorbar=False, ax=None): """Creates plot for contrast definition. Parameters ---------- contrast_def : str or array of shape (n_col) or list of (string or array of shape (n_col)) where ``n_col`` is the number of columns of the design matrix, (one array per run). If only one array is provided when there are several runs, it will be assumed that the same contrast is desired for all runs. The string can be a formula compatible with the linear constraint of the Patsy library. Basically one can use the name of the conditions as they appear in the design matrix of the fitted model combined with operators /*+- and numbers. Please checks the patsy documentation for formula examples: http://patsy.readthedocs.io/en/latest/API-reference.html#patsy.DesignInfo.linear_constraint design_matrix: pandas DataFrame colorbar: Boolean, optional (default False) Include a colorbar in the contrast matrix plot. ax: matplotlib Axes object, optional (default None) Directory where plotted figures will be stored. Returns ------- Plot Axes object """ design_column_names = design_matrix.columns.tolist() if isinstance(contrast_def, str): di = DesignInfo(design_column_names) contrast_def = di.linear_constraint(contrast_def).coefs if ax is None: plt.figure(figsize=(8, 4)) ax = plt.gca() maxval = np.max(np.abs(contrast_def)) con_mx = np.asmatrix(contrast_def) mat = ax.matshow(con_mx, aspect='equal', extent=[0, con_mx.shape[1], 0, con_mx.shape[0]], cmap='gray', vmin=-maxval, vmax=maxval) ax.set_label('conditions') ax.set_ylabel('') ax.set_yticklabels(['' for x in ax.get_yticklabels()]) # Shift ticks to be at 0.5, 1.5, etc ax.xaxis.set(ticks=np.arange(1.0, len(design_column_names) + 1.0), ticklabels=design_column_names) ax.set_xticklabels(design_column_names, rotation=90, ha='right') if colorbar: plt.colorbar(mat, fraction=0.025, pad=0.04) plt.tight_layout() return ax
def _get_contrast(second_level_contrast, design_matrix): """ Check and return contrast when testing one contrast at the time """ if isinstance(second_level_contrast, str): if second_level_contrast in design_matrix.columns.tolist(): contrast = second_level_contrast else: raise ValueError('"' + second_level_contrast + '" is not a valid' + ' contrast name') else: # Check contrast definition if second_level_contrast is None: if design_matrix.shape[1] == 1: second_level_contrast = np.ones([1]) else: raise ValueError('No second-level contrast is specified.') elif (np.nonzero(second_level_contrast)[0]).size != 1: raise ValueError('second_level_contrast must be ' 'a list of 0s and 1s') if isinstance(second_level_contrast, np.ndarray): con_val = np.asarray(second_level_contrast, dtype=bool) else: design_info = DesignInfo(design_matrix.columns.tolist()) constraint = design_info.linear_constraint(second_level_contrast) con_val = np.asarray(constraint.coefs, dtype=bool).ravel() contrast = np.asarray(design_matrix.columns.tolist())[con_val][0] return contrast
def compute_rfx_contrast(imgs, design_matrix, contrast_def, mask=None, noise_model='ols', stat_type='t', output_type='z_score'): design_info = DesignInfo(design_matrix.columns.tolist()) if isinstance(imgs, list): Y = np.stack([i.get_data() for i in imgs]).reshape(len(imgs), -1) elif isinstance(imgs, np.ndarray): Y = imgs else: raise ValueError(f"Unknown format for Y ({type(imgs)}).") X = design_matrix.values labels, results = run_glm(Y, X, noise_model=noise_model) if isinstance(contrast_def, (np.ndarray, str)): con_vals = [contrast_def] elif isinstance(contrast_def, (list, tuple)): con_vals = contrast_def else: raise ValueError('contrast_def must be an array or str or list of' ' (array or str)') for cidx, con in enumerate(con_vals): if not isinstance(con, np.ndarray): con_vals[cidx] = design_info.linear_constraint(con).coefs contrast = compute_contrast(labels, results, con_vals, stat_type) values = getattr(contrast, output_type)() if isinstance(imgs, list): values = nib.Nifti1Image(values.reshape(imgs[0].shape), affine=imgs[0].affine) return values
def _get_con_val(second_level_contrast, design_matrix): """ Check the contrast and return con_val when testing one contrast or more """ if second_level_contrast is None: if design_matrix.shape[1] == 1: second_level_contrast = np.ones([1]) else: raise ValueError('No second-level contrast is specified.') if isinstance(second_level_contrast, np.ndarray): con_val = second_level_contrast if np.all(con_val == 0): raise ValueError('Contrast is null') else: design_info = DesignInfo(design_matrix.columns.tolist()) constraint = design_info.linear_constraint(second_level_contrast) con_val = constraint.coefs return con_val
def compute_fxe_contrast(self, contrast_def, stat_type='t', run=None, output_type='z_score'): """ Computes a fixed effect across multiple runs. """ self.logger.info(f"Computing contrast: {contrast_def} for task {self.task} ...") if self.glm is None: raise ValueError("GLM has not been run yet!") if run is None: results = self.glm['results'] labels = self.glm['labels'] dms = self.glm['dms'] design_info = DesignInfo(dms[0].columns.tolist()) else: results = self.glm['results'][run] labels = self.glm['labels'][run] dms = self.glm['dms'][run] design_info = DesignInfo(dms.columns.tolist()) if isinstance(contrast_def, (np.ndarray, str)): con_vals = [contrast_def] elif isinstance(contrast_def, (list, tuple)): con_vals = contrast_def else: raise ValueError('contrast_def must be an array or str or list of' ' (array or str)') for cidx, con in enumerate(con_vals): if not isinstance(con, np.ndarray): con_vals[cidx] = design_info.linear_constraint(con).coefs if run is None: contrast = _fixed_effect_contrast(labels, results, con_vals, stat_type) else: contrast = compute_contrast(labels, results, con_vals, stat_type) values = getattr(contrast, output_type)() if self.mask is not None: return masking.unmask(values, self.mask) else: return values
def compute_contrast(self, second_level_contrast=None, first_level_contrast=None, second_level_stat_type=None, output_type='z_score'): """Generate different outputs corresponding to the contrasts provided e.g. z_map, t_map, effects and variance. Parameters ---------- second_level_contrast: str or array of shape (n_col), optional Where ``n_col`` is the number of columns of the design matrix, The string can be a formula compatible with the linear constraint of the Patsy library. Basically one can use the name of the conditions as they appear in the design matrix of the fitted model combined with operators /\*+- and numbers. Please check the patsy documentation for formula examples: http://patsy.readthedocs.io/en/latest/API-reference.html#patsy.DesignInfo.linear_constraint The default (None) is accepted if the design matrix has a single column, in which case the only possible contrast array([1]) is applied; when the design matrix has multiple columns, an error is raised. first_level_contrast: str or array of shape (n_col) with respect to FirstLevelModel, optional In case a list of FirstLevelModel was provided as second_level_input, we have to provide a contrast to apply to the first level models to get the corresponding list of images desired, that would be tested at the second level. In case a pandas DataFrame was provided as second_level_input this is the map name to extract from the pandas dataframe map_name column. It has to be a 't' contrast. second_level_stat_type: {'t', 'F'}, optional Type of the second level contrast output_type: str, optional Type of the output map. Can be 'z_score', 'stat', 'p_value', 'effect_size' or 'effect_variance' Returns ------- output_image: Nifti1Image The desired output image """ if self.second_level_input_ is None: raise ValueError('The model has not been fit yet') # first_level_contrast check if isinstance(self.second_level_input_[0], FirstLevelModel): if first_level_contrast is None: raise ValueError('If second_level_input was a list of ' 'FirstLevelModel, then first_level_contrast ' 'is mandatory. It corresponds to the ' 'second_level_contrast argument of the ' 'compute_contrast method of FirstLevelModel') # check contrast definition if second_level_contrast is None: if self.design_matrix_.shape[1] == 1: second_level_contrast = np.ones([1]) else: raise ValueError('No second-level contrast is specified.') if isinstance(second_level_contrast, np.ndarray): con_val = second_level_contrast if np.all(con_val == 0): raise ValueError('Contrast is null') else: design_info = DesignInfo(self.design_matrix_.columns.tolist()) constraint = design_info.linear_constraint(second_level_contrast) con_val = constraint.coefs # check output type if isinstance(output_type, _basestring): if output_type not in [ 'z_score', 'stat', 'p_value', 'effect_size', 'effect_variance' ]: raise ValueError( 'output_type must be one of "z_score", "stat"' ', "p_value", "effect_size" or "effect_variance"') else: raise ValueError('output_type must be one of "z_score", "stat",' ' "p_value", "effect_size" or "effect_variance"') # Get effect_maps appropriate for chosen contrast effect_maps = _infer_effect_maps(self.second_level_input_, first_level_contrast) # Check design matrix X and effect maps Y agree on number of rows if len(effect_maps) != self.design_matrix_.shape[0]: raise ValueError( 'design_matrix does not match the number of maps considered. ' '%i rows in design matrix do not match with %i maps' % (self.design_matrix_.shape[0], len(effect_maps))) # Fit an Ordinary Least Squares regression for parametric statistics Y = self.masker_.transform(effect_maps) if self.memory: mem_glm = self.memory.cache(run_glm, ignore=['n_jobs']) else: mem_glm = run_glm labels, results = mem_glm(Y, self.design_matrix_.values, n_jobs=self.n_jobs, noise_model='ols') # We save memory if inspecting model details is not necessary if self.minimize_memory: for key in results: results[key] = SimpleRegressionResults(results[key]) self.labels_ = labels self.results_ = results # We compute contrast object if self.memory: mem_contrast = self.memory.cache(compute_contrast) else: mem_contrast = compute_contrast contrast = mem_contrast(self.labels_, self.results_, con_val, second_level_stat_type) # We get desired output from contrast object estimate_ = getattr(contrast, output_type)() # Prepare the returned images output = self.masker_.inverse_transform(estimate_) contrast_name = str(con_val) output.header['descrip'] = ('%s of contrast %s' % (output_type, contrast_name)) return output
def compute_contrast(self, contrast_def, stat_type=None, output_type='z_score'): """Generate different outputs corresponding to the contrasts provided e.g. z_map, t_map, effects and variance. In multi-session case, outputs the fixed effects map. Parameters ---------- contrast_def : str or array of shape (n_col) or list of (string or array of shape (n_col)) where ``n_col`` is the number of columns of the design matrix, (one array per run). If only one array is provided when there are several runs, it will be assumed that the same contrast is desired for all runs. The string can be a formula compatible with the linear constraint of the Patsy library. Basically one can use the name of the conditions as they appear in the design matrix of the fitted model combined with operators /\*+- and numbers. Please checks the patsy documentation for formula examples: http://patsy.readthedocs.io/en/latest/API-reference.html#patsy.DesignInfo.linear_constraint stat_type : {'t', 'F'}, optional type of the contrast output_type : str, optional Type of the output map. Can be 'z_score', 'stat', 'p_value', 'effect_size', 'effect_variance' or 'all' Returns ------- output : Nifti1Image or dict The desired output image(s). If ``output_type == 'all'``, then the output is a dictionary of images, keyed by the type of image. """ if self.labels_ is None or self.results_ is None: raise ValueError('The model has not been fit yet') if isinstance(contrast_def, (np.ndarray, str)): con_vals = [contrast_def] elif isinstance(contrast_def, (list, tuple)): con_vals = contrast_def else: raise ValueError('contrast_def must be an array or str or list of' ' (array or str)') # Translate formulas to vectors with patsy design_info = DesignInfo(self.design_matrices_[0].columns.tolist()) for cidx, con in enumerate(con_vals): if not isinstance(con, np.ndarray): con_vals[cidx] = design_info.linear_constraint(con).coefs n_runs = len(self.labels_) if len(con_vals) != n_runs: warn('One contrast given, assuming it for all %d runs' % n_runs) con_vals = con_vals * n_runs # 'all' is assumed to be the final entry; if adding more, place before 'all' valid_types = [ 'z_score', 'stat', 'p_value', 'effect_size', 'effect_variance', 'all' ] if output_type not in valid_types: raise ValueError( 'output_type must be one of {}'.format(valid_types)) contrast = _fixed_effect_contrast(self.labels_, self.results_, con_vals, stat_type) output_types = valid_types[:-1] if output_type == 'all' else [ output_type ] outputs = {} for output_type_ in output_types: estimate_ = getattr(contrast, output_type_)() # Prepare the returned images output = self.masker_.inverse_transform(estimate_) contrast_name = str(con_vals) output.header['descrip'] = ('%s of contrast %s' % (output_type_, contrast_name)) outputs[output_type_] = output return outputs if output_type == 'all' else output
def compute_contrast(self, contrast_def, stat_type=None, output_type='z_score'): """Generate different outputs corresponding to the contrasts provided e.g. z_map, t_map, effects and variance. In multi-session case, outputs the fixed effects map. Parameters ---------- contrast_def : str or array of shape (n_col) or list of (string or array of shape (n_col)) where ``n_col`` is the number of columns of the design matrix, (one array per run). If only one array is provided when there are several runs, it will be assumed that the same contrast is desired for all runs. The string can be a formula compatible with the linear constraint of the Patsy library. Basically one can use the name of the conditions as they appear in the design matrix of the fitted model combined with operators /\*+- and numbers. Please checks the patsy documentation for formula examples: http://patsy.readthedocs.io/en/latest/API-reference.html#patsy.DesignInfo.linear_constraint stat_type : {'t', 'F'}, optional type of the contrast output_type : str, optional Type of the output map. Can be 'z_score', 'stat', 'p_value', 'effect_size', 'effect_variance' or 'all' Returns ------- output : Nifti1Image or dict The desired output image(s). If ``output_type == 'all'``, then the output is a dictionary of images, keyed by the type of image. """ if self.labels_ is None or self.results_ is None: raise ValueError('The model has not been fit yet') if isinstance(contrast_def, (np.ndarray, str)): con_vals = [contrast_def] elif isinstance(contrast_def, (list, tuple)): con_vals = contrast_def else: raise ValueError('contrast_def must be an array or str or list of' ' (array or str)') # Translate formulas to vectors with patsy design_info = DesignInfo(self.design_matrices_[0].columns.tolist()) for cidx, con in enumerate(con_vals): if not isinstance(con, np.ndarray): con_vals[cidx] = design_info.linear_constraint(con).coefs n_runs = len(self.labels_) if len(con_vals) != n_runs: warn('One contrast given, assuming it for all %d runs' % n_runs) con_vals = con_vals * n_runs # 'all' is assumed to be the final entry; if adding more, place before 'all' valid_types = ['z_score', 'stat', 'p_value', 'effect_size', 'effect_variance', 'all'] if output_type not in valid_types: raise ValueError('output_type must be one of {}'.format(valid_types)) contrast = _fixed_effect_contrast(self.labels_, self.results_, con_vals, stat_type) output_types = valid_types[:-1] if output_type == 'all' else [output_type] outputs = {} for output_type_ in output_types: estimate_ = getattr(contrast, output_type_)() # Prepare the returned images output = self.masker_.inverse_transform(estimate_) contrast_name = str(con_vals) output.header['descrip'] = ( '%s of contrast %s' % (output_type_, contrast_name)) outputs[output_type_] = output return outputs if output_type == 'all' else output
def compute_contrast(self, contrast_def, stat_type=None, output_type='z_score'): """Generate different outputs corresponding to the contrasts provided e.g. z_map, t_map, effects and variance. Parameters ---------- contrast_def : str or array of shape (n_col) where ``n_col`` is the number of columns of the design matrix, The string can be a formula compatible with the linear constraint of the Patsy library. Basically one can use the name of the conditions as they appear in the design matrix of the fitted model combined with operators /*+- and numbers. Please checks the patsy documentation for formula examples: http://patsy.readthedocs.io/en/latest/API-reference.html#patsy.DesignInfo.linear_constraint stat_type : {'t', 'F'}, optional type of the contrast output_type : str, optional Type of the output map. Can be 'z_score', 'stat', 'p_value', 'effect_size' or 'effect_variance' Returns ------- output_image : Nifti1Image The desired output image """ # check model was fit if self.labels_ is None or self.results_ is None: raise ValueError('The model has not been fit yet') # check contrast definition if isinstance(contrast_def, np.ndarray): con_val = contrast_def if np.all(con_val == 0): raise ValueError('Contrast is null') else: design_info = DesignInfo(self.design_matrix_.columns.tolist()) con_val = design_info.linear_constraint(contrast_def).coefs # check output type if isinstance(output_type, _basestring): if output_type not in [ 'z_score', 'stat', 'p_value', 'effect_size', 'effect_variance' ]: raise ValueError( 'output_type must be one of "z_score", "stat"' ', "p_value", "effect_size" or "effect_variance"') else: raise ValueError('output_type must be one of "z_score", "stat",' ' "p_value", "effect_size" or "effect_variance"') if self.memory is not None: arg_ignore = ['labels', 'results'] mem_contrast = self.memory.cache(compute_contrast, ignore=arg_ignore) else: mem_contrast = compute_contrast contrast = mem_contrast(self.labels_, self.results_, con_val, stat_type) estimate_ = getattr(contrast, output_type)() # Prepare the returned images output = self.masker_.inverse_transform(estimate_) contrast_name = str(con_val) output.get_header()['descrip'] = ('%s of contrast %s' % (output_type, contrast_name)) return output
def compute_contrast( self, second_level_contrast=None, first_level_contrast=None, second_level_stat_type=None, output_type='z_score'): """Generate different outputs corresponding to the contrasts provided e.g. z_map, t_map, effects and variance. Parameters ---------- second_level_contrast: str or array of shape (n_col), optional Where ``n_col`` is the number of columns of the design matrix, The string can be a formula compatible with the linear constraint of the Patsy library. Basically one can use the name of the conditions as they appear in the design matrix of the fitted model combined with operators /\*+- and numbers. Please check the patsy documentation for formula examples: http://patsy.readthedocs.io/en/latest/API-reference.html#patsy.DesignInfo.linear_constraint The default (None) is accepted if the design matrix has a single column, in which case the only possible contrast array([1]) is applied; when the design matrix has multiple columns, an error is raised. first_level_contrast: str or array of shape (n_col) with respect to FirstLevelModel, optional In case a list of FirstLevelModel was provided as second_level_input, we have to provide a contrast to apply to the first level models to get the corresponding list of images desired, that would be tested at the second level. In case a pandas DataFrame was provided as second_level_input this is the map name to extract from the pandas dataframe map_name column. It has to be a 't' contrast. second_level_stat_type: {'t', 'F'}, optional Type of the second level contrast output_type: str, optional Type of the output map. Can be 'z_score', 'stat', 'p_value', 'effect_size' or 'effect_variance' Returns ------- output_image: Nifti1Image The desired output image """ if self.second_level_input_ is None: raise ValueError('The model has not been fit yet') # first_level_contrast check if isinstance(self.second_level_input_[0], FirstLevelModel): if first_level_contrast is None: raise ValueError('If second_level_input was a list of ' 'FirstLevelModel, then first_level_contrast ' 'is mandatory. It corresponds to the ' 'second_level_contrast argument of the ' 'compute_contrast method of FirstLevelModel') # check contrast definition if second_level_contrast is None: if self.design_matrix_.shape[1] == 1: second_level_contrast = np.ones([1]) else: raise ValueError('No second-level contrast is specified.') if isinstance(second_level_contrast, np.ndarray): con_val = second_level_contrast if np.all(con_val == 0): raise ValueError('Contrast is null') else: design_info = DesignInfo(self.design_matrix_.columns.tolist()) constraint = design_info.linear_constraint(second_level_contrast) con_val = constraint.coefs # check output type if isinstance(output_type, _basestring): if output_type not in ['z_score', 'stat', 'p_value', 'effect_size', 'effect_variance']: raise ValueError( 'output_type must be one of "z_score", "stat"' ', "p_value", "effect_size" or "effect_variance"') else: raise ValueError('output_type must be one of "z_score", "stat",' ' "p_value", "effect_size" or "effect_variance"') # Get effect_maps appropriate for chosen contrast effect_maps = _infer_effect_maps(self.second_level_input_, first_level_contrast) # Check design matrix X and effect maps Y agree on number of rows if len(effect_maps) != self.design_matrix_.shape[0]: raise ValueError( 'design_matrix does not match the number of maps considered. ' '%i rows in design matrix do not match with %i maps' % (self.design_matrix_.shape[0], len(effect_maps))) # Fit an Ordinary Least Squares regression for parametric statistics Y = self.masker_.transform(effect_maps) if self.memory: mem_glm = self.memory.cache(run_glm, ignore=['n_jobs']) else: mem_glm = run_glm labels, results = mem_glm(Y, self.design_matrix_.values, n_jobs=self.n_jobs, noise_model='ols') # We save memory if inspecting model details is not necessary if self.minimize_memory: for key in results: results[key] = SimpleRegressionResults(results[key]) self.labels_ = labels self.results_ = results # We compute contrast object if self.memory: mem_contrast = self.memory.cache(compute_contrast) else: mem_contrast = compute_contrast contrast = mem_contrast(self.labels_, self.results_, con_val, second_level_stat_type) # We get desired output from contrast object estimate_ = getattr(contrast, output_type)() # Prepare the returned images output = self.masker_.inverse_transform(estimate_) contrast_name = str(con_val) output.header['descrip'] = ( '%s of contrast %s' % (output_type, contrast_name)) return output