def test_get_clusters_table_not_modifying_stat_image(): shape = (9, 10, 11) data = np.zeros(shape) data[2:4, 5:7, 6:8] = 5. data[0:3, 0:3, 0:3] = 6. stat_img = nib.Nifti1Image(data, np.eye(4)) data_orig = get_data(stat_img).copy() # test one cluster should be removed clusters_table = get_clusters_table(stat_img, 4, cluster_threshold=10) assert np.allclose(data_orig, get_data(stat_img)) assert len(clusters_table) == 1 # test no clusters should be removed stat_img = nib.Nifti1Image(data, np.eye(4)) clusters_table = get_clusters_table(stat_img, 4, cluster_threshold=7) assert np.allclose(data_orig, get_data(stat_img)) assert len(clusters_table) == 2 # test cluster threshold is None stat_img = nib.Nifti1Image(data, np.eye(4)) clusters_table = get_clusters_table(stat_img, 4, cluster_threshold=None) assert np.allclose(data_orig, get_data(stat_img)) assert len(clusters_table) == 2
def test_get_clusters_table(tmp_path): shape = (9, 10, 11) data = np.zeros(shape) data[2:4, 5:7, 6:8] = 5. stat_img = nib.Nifti1Image(data, np.eye(4)) # test one cluster extracted cluster_table = get_clusters_table(stat_img, 4, 0) assert len(cluster_table) == 1 # test empty table on high stat threshold cluster_table = get_clusters_table(stat_img, 6, 0) assert len(cluster_table) == 0 # test empty table on high cluster threshold cluster_table = get_clusters_table(stat_img, 4, 9) assert len(cluster_table) == 0 # test with filename fname = str(tmp_path / "stat_img.nii.gz") stat_img.to_filename(fname) cluster_table = get_clusters_table(fname, 4, 0) assert len(cluster_table) == 1 # test with extra dimension data_extra_dim = data[..., np.newaxis] stat_img_extra_dim = nib.Nifti1Image(data_extra_dim, np.eye(4)) cluster_table = get_clusters_table(stat_img_extra_dim, 4, 0) assert len(cluster_table) == 1
def test_get_clusters_table(): shape = (9, 10, 11) data = np.zeros(shape) data[2:4, 5:7, 6:8] = 5. stat_img = nib.Nifti1Image(data, np.eye(4)) # test one cluster extracted cluster_table = get_clusters_table(stat_img, 4, 0) assert len(cluster_table) == 1 # test empty table on high stat threshold cluster_table = get_clusters_table(stat_img, 6, 0) assert len(cluster_table) == 0 # test empty table on high cluster threshold cluster_table = get_clusters_table(stat_img, 4, 9) assert len(cluster_table) == 0
def test_get_clusters_table(tmp_path): shape = (9, 10, 11) data = np.zeros(shape) data[2:4, 5:7, 6:8] = 5. data[4:6, 7:9, 8:10] = -5. stat_img = nib.Nifti1Image(data, np.eye(4)) # test one cluster extracted cluster_table = get_clusters_table(stat_img, 4, 0, two_sided=False) assert len(cluster_table) == 1 # test empty table on high stat threshold cluster_table = get_clusters_table(stat_img, 6, 0, two_sided=False) assert len(cluster_table) == 0 # test empty table on high cluster threshold cluster_table = get_clusters_table(stat_img, 4, 9, two_sided=False) assert len(cluster_table) == 0 # test two clusters with different signs extracted cluster_table = get_clusters_table(stat_img, 4, 0, two_sided=True) assert len(cluster_table) == 2 # test empty table on high stat threshold cluster_table = get_clusters_table(stat_img, 6, 0, two_sided=True) assert len(cluster_table) == 0 # test empty table on high cluster threshold cluster_table = get_clusters_table(stat_img, 4, 9, two_sided=True) assert len(cluster_table) == 0 # test with filename fname = str(tmp_path / "stat_img.nii.gz") stat_img.to_filename(fname) cluster_table = get_clusters_table(fname, 4, 0, two_sided=True) assert len(cluster_table) == 2 # test with extra dimension data_extra_dim = data[..., np.newaxis] stat_img_extra_dim = nib.Nifti1Image(data_extra_dim, np.eye(4)) cluster_table = get_clusters_table(stat_img_extra_dim, 4, 0, two_sided=True) assert len(cluster_table) == 2 # Test that nans are handled correctly (No numpy axis errors are raised) data[data == 0] = np.nan stat_img_nans = nib.Nifti1Image(data, affine=np.eye(4)) cluster_table = get_clusters_table(stat_img_nans, 1e-2, 0, two_sided=False) assert len(cluster_table) == 1
from nilearn.reporting import plot_contrast_matrix plot_contrast_matrix('StopSuccess - Go', design_matrix) plotting.plot_glass_brain(z_map, colorbar=True, threshold=norm.isf(0.001), plot_abs=False, display_mode='z', figure=plt.figure(figsize=(4, 4))) plt.show() ############################################################################### # We can get a latex table from a Pandas Dataframe for display and publication purposes from nilearn.reporting import get_clusters_table print(get_clusters_table(z_map, norm.isf(0.001), 10).to_latex()) ######################################################################### # Generating a report # ------------------- # Using the computed FirstLevelModel and contrast information, # we can quickly create a summary report. from nilearn.reporting import make_glm_report report = make_glm_report( model=model, contrasts='StopSuccess - Go', ) #########################################################################
def _make_stat_maps_contrast_clusters(stat_img, contrasts_plots, threshold, alpha, cluster_threshold, height_control, min_distance, bg_img, display_mode, plot_type): """ Populates a smaller HTML sub-template with the proper values, make a list containing one or more of such components & returns the list to be inserted into the HTML Report Template. Each component contains the HTML code for a contrast & its corresponding statistical maps & cluster table; Parameters ---------- stat_img : Niimg-like object or None statistical image (presumably in z scale) whenever height_control is 'fpr' or None, stat_img=None is acceptable. If it is 'fdr' or 'bonferroni', an error is raised if stat_img is None. contrasts_plots: Dict[str, str] Contains contrast names & HTML code of the contrast's SVG plot. threshold: float desired threshold in z-scale. This is used only if height_control is None alpha: float number controlling the thresholding (either a p-value or q-value). Its actual meaning depends on the height_control parameter. This function translates alpha to a z-scale threshold. cluster_threshold : float cluster size threshold. In the returned thresholded map, sets of connected voxels (`clusters`) with size smaller than this number will be removed. height_control: string false positive control meaning of cluster forming threshold: 'fpr' or 'fdr' or 'bonferroni' or None min_distance: `float` For display purposes only. Minimum distance between subpeaks in mm. Default is 8 mm. bg_img : Niimg-like object Only used when plot_type is 'slice'. See http://nilearn.github.io/manipulating_images/input_output.html The background image for stat maps to be plotted on upon. If nothing is specified, the MNI152 template will be used. To turn off background image, just pass "bg_img=False". display_mode: string Choose the direction of the cuts: 'x' - sagittal, 'y' - coronal, 'z' - axial, 'l' - sagittal left hemisphere only, 'r' - sagittal right hemisphere only, 'ortho' - three cuts are performed in orthogonal directions. Possible values are: 'ortho', 'x', 'y', 'z', 'xz', 'yx', 'yz', 'l', 'r', 'lr', 'lzr', 'lyr', 'lzry', 'lyrz'. plot_type: string ['slice', 'glass'] The type of plot to be drawn. Returns ------- all_components: List[String] Each element is a set of HTML code for contrast name, contrast plot, statistical map, cluster table. """ all_components = [] components_template_path = os.path.join( HTML_TEMPLATE_ROOT_PATH, 'stat_maps_contrast_clusters_template.html') with open(components_template_path) as html_template_obj: components_template_text = html_template_obj.read() for contrast_name, stat_map_img in stat_img.items(): component_text_ = string.Template(components_template_text) thresholded_stat_map, threshold = threshold_stats_img( stat_img=stat_map_img, threshold=threshold, alpha=alpha, cluster_threshold=cluster_threshold, height_control=height_control, ) table_details = _clustering_params_to_dataframe( threshold, cluster_threshold, min_distance, height_control, alpha, ) stat_map_svg = _stat_map_to_svg( stat_img=thresholded_stat_map, bg_img=bg_img, display_mode=display_mode, plot_type=plot_type, table_details=table_details, ) cluster_table = get_clusters_table( stat_map_img, stat_threshold=threshold, cluster_threshold=cluster_threshold, min_distance=min_distance, ) cluster_table_html = _dataframe_to_html( cluster_table, precision=2, index=False, classes='cluster-table', ) table_details_html = _dataframe_to_html( table_details, precision=2, header=False, classes='cluster-details-table', ) components_values = { 'contrast_name': escape(contrast_name), 'contrast_plot': contrasts_plots[contrast_name], 'stat_map_img': stat_map_svg, 'cluster_table_details': table_details_html, 'cluster_table': cluster_table_html, } component_text_ = component_text_.safe_substitute(**components_values) all_components.append(component_text_) return all_components
display_mode='z', cut_coords=3, black_bg=True, title='Active minus Rest (fdr=0.05), clusters > 10 voxels') plt.show() ############################################################################### # We can save the effect and zscore maps to the disk. z_map.to_filename(join(outdir, 'active_vs_rest_z_map.nii.gz')) eff_map.to_filename(join(outdir, 'active_vs_rest_eff_map.nii.gz')) ############################################################################### # We can furthermore extract and report the found positions in a table. from nilearn.reporting import get_clusters_table table = get_clusters_table(z_map, stat_threshold=threshold, cluster_threshold=20) print(table) ############################################################################### # This table can be saved for future use. table.to_csv(join(outdir, 'table.csv')) ############################################################################### # Performing an F-test. # # "active vs rest" is a typical t test: condition versus # baseline. Another popular type of test is an F test in which one # seeks whether a certain combination of conditions (possibly two-, # three- or higher-dimensional) explains a significant proportion of # the signal. Here one might for instance test which voxels are well
# Calculate and plot contrast # --------------------------- from nilearn import plotting z_map = fmri_glm.compute_contrast('active - rest') plotting.plot_stat_map(z_map, bg_img=mean_img, threshold=3.1) ######################################################################### # Extract the largest clusters # ---------------------------- from nilearn.reporting import get_clusters_table from nilearn.maskers import NiftiSpheresMasker table = get_clusters_table(z_map, stat_threshold=3.1, cluster_threshold=20).set_index('Cluster ID', drop=True) table.head() # get the 6 largest clusters' max x, y, and z coordinates coords = table.loc[range(1, 7), ['X', 'Y', 'Z']].values # extract time series from each coordinate masker = NiftiSpheresMasker(coords) real_timeseries = masker.fit_transform(fmri_img) predicted_timeseries = masker.fit_transform(fmri_glm.predicted[0]) ######################################################################### # Plot predicted and actual time series for 6 most significant clusters # --------------------------------------------------------------------- import matplotlib.pyplot as plt
def transform(self, dataset): """Create coordinate peaks from statistical images. Parameters ---------- dataset : :obj:`nimare.dataset.Dataset` Dataset with z maps and/or p maps that can be converted to coordinates. Returns ------- dataset : :obj:`nimare.dataset.Dataset` Dataset with coordinates generated from images and metadata indicating origin of coordinates ('original' or 'nimare'). """ # relevant variables from dataset space = dataset.space masker = dataset.masker images_df = dataset.images metadata = dataset.metadata.copy() # conform space specification if "mni" in space.lower() or "ale" in space.lower(): coordinate_space = "MNI" elif "tal" in space.lower(): coordinate_space = "TAL" else: coordinate_space = None coordinates_dict = {} for _, row in images_df.iterrows(): if row["id"] in list(dataset.coordinates["id"]) and self.merge_strategy == "fill": continue if row.get("z"): clusters = get_clusters_table( nib.funcs.squeeze_image(nib.load(row.get("z"))), self.z_threshold, self.cluster_threshold, self.two_sided, self.min_distance, ) elif row.get("p"): LGR.info( f"No Z map for {row['id']}, using p map " "(p-values will be treated as positive z-values)" ) if self.two_sided: LGR.warning(f"Cannot use two_sided threshold using a p map for {row['id']}") p_threshold = 1 - z_to_p(self.z_threshold) nimg = nib.funcs.squeeze_image(nib.load(row.get("p"))) inv_nimg = nib.Nifti1Image(1 - nimg.get_fdata(), nimg.affine, nimg.header) clusters = get_clusters_table( inv_nimg, p_threshold, self.cluster_threshold, self.min_distance, ) # Peak stat p-values are reported as 1 - p in get_clusters_table clusters["Peak Stat"] = p_to_z(1 - clusters["Peak Stat"]) else: LGR.warning(f"No Z or p map for {row['id']}, skipping...") continue # skip entry if no clusters are found if clusters.empty: LGR.warning( f"No clusters were found for {row['id']} at a threshold of {self.z_threshold}" ) continue if self.remove_subpeaks: # subpeaks are identified as 1a, 1b, etc # while peaks are kept as 1, 2, 3, etc, # so removing all non-int rows will # keep main peaks while removing subpeaks clusters = clusters[clusters["Cluster ID"].apply(lambda x: isinstance(x, int))] coordinates_dict[row["study_id"]] = { "contrasts": { row["contrast_id"]: { "coords": { "space": coordinate_space, "x": list(clusters["X"]), "y": list(clusters["Y"]), "z": list(clusters["Z"]), "z_stat": list(clusters["Peak Stat"]), }, "metadata": {"coordinate_source": "nimare"}, } } } # only the generated coordinates ('demolish') coordinates_df = dict_to_coordinates(coordinates_dict, masker, space) meta_df = dict_to_df( pd.DataFrame(dataset._ids), coordinates_dict, "metadata", ) if "coordinate_source" in meta_df.columns: metadata["coordinate_source"] = meta_df["coordinate_source"] else: # nimare did not overwrite any coordinates metadata["coordinate_source"] = ["original"] * metadata.shape[0] if self.merge_strategy != "demolish": original_idxs = ~dataset.coordinates["id"].isin(coordinates_df["id"]) old_coordinates_df = dataset.coordinates[original_idxs] coordinates_df = coordinates_df.append(old_coordinates_df, ignore_index=True) # specify original coordinates original_ids = set(old_coordinates_df["id"]) metadata.loc[metadata["id"].isin(original_ids), "coordinate_source"] = "original" # ensure z_stat is treated as float if "z_stat" in coordinates_df.columns: coordinates_df["z_stat"] = coordinates_df["z_stat"].astype(float) new_dataset = copy.deepcopy(dataset) new_dataset.coordinates = coordinates_df new_dataset.metadata = metadata return new_dataset