def generate_subject_stats_report( stats_report_filename, contrasts, z_maps, mask, design_matrices=None, subject_id=None, anat=None, anat_affine=None, slicer="z", cut_coords=None, statistical_mapping_trick=False, threshold=2.3, cluster_th=0, cmap=viz.cm.cold_hot, start_time=None, title=None, user_script_name=None, progress_logger=None, shutdown_all_reloaders=True, **glm_kwargs ): """Generates a report summarizing the statistical methods and results Parameters ---------- stats_report_filename: string: html file to which output (generated html) will be written contrasts: dict of arrays contrasts we are interested in; same number of contrasts as zmaps; same keys zmaps: dict of image objects or strings (image filenames) zmaps for contrasts we are interested in; one per contrast id mask: 'nifti image object' brain mask for ROI design_matrix: list of 'DesignMatrix', `numpy.ndarray` objects or of strings (.png, .npz, etc.) for filenames design matrices for the experimental conditions contrasts: dict of arrays dictionary of contrasts of interest; the keys are the contrast ids, the values are contrast values (lists) z_maps: dict of 3D image objects or strings (image filenames) dict with same keys as 'contrasts'; the values are paths of z-maps for the respective contrasts anat: 3D array (optional) brain image to serve bg unto which activation maps will be plotted; passed to viz.plot_map API anat_affine: 2D array (optional) affine data for the anat threshold: float (optional) threshold to be applied to activation maps voxel-wise cluster_th: int (optional) minimal voxel count for clusteres declared as 'activated' cmap: cmap object (default viz.cm.cold_hot) color-map to use in plotting activation maps start_time: string (optional) start time for the stats analysis (useful for the generated report page) user_script_name: string (optional, default None) existing filename, path to user script used in doing the analysis progress_logger: ProgressLogger object (optional) handle for logging progress shutdown_all_reloaders: bool (optional, default True) if True, all pages connected to the stats report page will be prevented from reloading after the stats report page has been completely generated **glm_kwargs: kwargs used to specify the control parameters used to specify the experimental paradigm and the GLM """ # Delayed import of nipy for more robustness when it is not present from nipy.modalities.fmri.design_matrix import DesignMatrix if slicer == "ortho": statistical_mapping_trick = False if not hasattr(cut_coords, "__iter__"): cut_coords = None # prepare for stats reporting if progress_logger is None: progress_logger = base_reporter.ProgressReport() output_dir = os.path.dirname(stats_report_filename) if not os.path.exists(output_dir): os.makedirs(output_dir) # copy css and js stuff to output dir base_reporter.copy_web_conf_files(output_dir) # initialize gallery of design matrices design_thumbs = base_reporter.ResultsGallery( loader_filename=os.path.join(output_dir, "design.html") ) # initialize gallery of activation maps activation_thumbs = base_reporter.ResultsGallery( loader_filename=os.path.join(output_dir, "activation.html") ) # get caller module handle from stack-frame if user_script_name is None: user_script_name = sys.argv[0] user_source_code = base_reporter.get_module_source_code( user_script_name) methods = """ GLM and Statistical Inference have been done using the <i>%s</i> script, \ powered by <a href="%s">nipy</a>. Statistic images have been thresholded at \ Z>%s voxel-level. """ % (user_script_name, base_reporter.NIPY_URL, threshold) # report the control parameters used in the paradigm and analysis design_params = "" glm_kwargs["contrasts"] = contrasts if len(glm_kwargs): design_params += ("The following control parameters were used for " " specifying the experimental paradigm and fitting the " "GLM:<br/><ul>") design_params += base_reporter.dict_to_html_ul(glm_kwargs) if start_time is None: start_time = base_reporter.pretty_time() if title is None: title = "GLM and Statistical Inference" if not subject_id is None: title += " for subject %s" % subject_id level1_html_markup = base_reporter.get_subject_report_stats_html_template( title=title, start_time=start_time, subject_id=subject_id, # insert source code stub source_script_name=user_script_name, source_code=user_source_code, design_params=design_params, methods=methods, threshold=threshold, cmap=cmap.name) with open(stats_report_filename, 'w') as fd: fd.write(str(level1_html_markup)) fd.close() progress_logger.log("<b>Level 1 statistics</b><br/><br/>") # create design matrix thumbs if not design_matrices is None: if not hasattr(design_matrices, '__len__'): design_matrices = [design_matrices] for design_matrix, j in zip(design_matrices, xrange(len(design_matrices))): # sanitize design_matrix type if isinstance(design_matrix, basestring): if not isinstance(design_matrix, DesignMatrix): if design_matrix.endswith('.npz'): npz = np.load(design_matrix) design_matrix = DesignMatrix(npz['X'], npz['conditions'], ) else: # XXX handle case of .png, jpeg design matrix image raise TypeError( "Unsupported design matrix type: %s" % type( design_matrix)) elif isinstance(design_matrix, np.ndarray) or isinstance( design_matrix, list): X = np.array(design_matrix) # assert len(X.shape) == 2 conditions = ['%i' % i for i in xrange(X.shape[-1])] design_matrix = DesignMatrix(X, conditions) # else: # raise TypeError( # "Unsupported design matrix type '%s'" % type( # design_matrix)) # plot design_matrix proper ax = design_matrix.show(rescale=True) ax.set_position([.05, .25, .9, .65]) dmat_outfile = os.path.join(output_dir, 'design_matrix_%i.png' % (j + 1), ) pl.savefig(dmat_outfile, bbox_inches="tight", dpi=200) pl.close() thumb = base_reporter.Thumbnail() thumb.a = base_reporter.a(href=os.path.basename(dmat_outfile)) thumb.img = base_reporter.img(src=os.path.basename(dmat_outfile), height="500px", ) thumb.description = "Design Matrix" thumb.description += " %s" % (j + 1) if len( design_matrices) > 1 else "" # commit activation thumbnail into gallery design_thumbs.commit_thumbnails(thumb) # make colorbar (place-holder, will be overridden, once we've figured out # the correct end points) for activations colorbar_outfile = os.path.join(output_dir, 'activation_colorbar.png') base_reporter.make_standalone_colorbar( cmap, threshold, 8., colorbar_outfile) # create activation thumbs _vmax = 0 _vmin = threshold for contrast_id, contrast_val in contrasts.iteritems(): z_map = z_maps[contrast_id] # load the map if isinstance(z_map, basestring): z_map = nibabel.load(z_map) # compute vmin and vmax vmin, vmax = base_reporter.compute_vmin_vmax(z_map.get_data()) # update colorbar endpoints _vmax = max(_vmax, vmax) _vmin = min(_vmin, vmin) # sanitize anat if not anat is None: if anat.ndim == 4: assert anat.shape[-1] == 1, ( "Expecting 3D array for ant, got shape %s" % str( anat.shape)) anat = anat[..., 0] else: assert anat.ndim == 3, ( "Expecting 3D array for ant, got shape %s" % str( anat.shape)) # generate level 1 stats table title = "Level 1 stats for %s contrast" % contrast_id stats_table = os.path.join(output_dir, "%s_stats_table.html" % ( contrast_id)) z_clusters = generate_level1_stats_table( z_map, mask, stats_table, cluster_th=cluster_th, z_threshold=threshold, title=title, ) # compute cut_coords if statistical_mapping_trick: slicer = slicer.lower() if slicer in ["x", "y", "z"] and cut_coords is None or isinstance( cut_coords, int): axis = 'xyz'.index(slicer) if cut_coords is None: cut_coords = min(5, len(z_clusters)) _cut_coords = [x for zc in z_clusters for x in list( set(zc['maxima'][..., axis]))] # # _cut_coords = _maximally_separated_subset( # # _cut_coords, cut_coords) # assert len(_cut_coords) == cut_coords cut_coords = _cut_coords # plot activation proper viz.plot_map(z_map.get_data(), z_map.get_affine(), cmap=cmap, anat=anat, anat_affine=anat_affine, threshold=threshold, slicer=slicer, cut_coords=cut_coords, black_bg=True ) # store activation plot z_map_plot = os.path.join(output_dir, "%s_z_map.png" % contrast_id) pl.savefig(z_map_plot, dpi=200, bbox_inches='tight', facecolor="k", edgecolor="k") pl.close() # create thumbnail for activation thumbnail = base_reporter.Thumbnail() thumbnail.a = base_reporter.a(href=os.path.basename(stats_table)) thumbnail.img = base_reporter.img(src=os.path.basename(z_map_plot), height="150px",) thumbnail.description = contrast_id activation_thumbs.commit_thumbnails(thumbnail) # make colorbar for activations base_reporter.make_standalone_colorbar( cmap, _vmin, _vmax, colorbar_outfile) # we're done, shut down re-loaders progress_logger.log('<hr/>') # prevent stats report page from reloading henceforth progress_logger.finish(stats_report_filename) # prevent any related page from reloading if shutdown_all_reloaders: progress_logger.finish_dir(output_dir) # return generated html with open(stats_report_filename, 'r') as fd: stats_report = fd.read() fd.close() return stats_report
def generate_subject_stats_report(stats_report_filename, contrasts, z_maps, mask, design_matrices=None, subject_id=None, anat=None, anat_affine=None, slicer="z", cut_coords=None, statistical_mapping_trick=False, threshold=2.3, cluster_th=0, cmap=viz.cm.cold_hot, start_time=None, title=None, user_script_name=None, progress_logger=None, shutdown_all_reloaders=True, **glm_kwargs): """Generates a report summarizing the statistical methods and results Parameters ---------- stats_report_filename: string: html file to which output (generated html) will be written contrasts: dict of arrays contrasts we are interested in; same number of contrasts as zmaps; same keys zmaps: dict of image objects or strings (image filenames) zmaps for contrasts we are interested in; one per contrast id mask: 'nifti image object' brain mask for ROI design_matrix: list of 'DesignMatrix', `numpy.ndarray` objects or of strings (.png, .npz, etc.) for filenames design matrices for the experimental conditions contrasts: dict of arrays dictionary of contrasts of interest; the keys are the contrast ids, the values are contrast values (lists) z_maps: dict of 3D image objects or strings (image filenames) dict with same keys as 'contrasts'; the values are paths of z-maps for the respective contrasts anat: 3D array (optional) brain image to serve bg unto which activation maps will be plotted; passed to viz.plot_map API anat_affine: 2D array (optional) affine data for the anat threshold: float (optional) threshold to be applied to activation maps voxel-wise cluster_th: int (optional) minimal voxel count for clusteres declared as 'activated' cmap: cmap object (default viz.cm.cold_hot) color-map to use in plotting activation maps start_time: string (optional) start time for the stats analysis (useful for the generated report page) user_script_name: string (optional, default None) existing filename, path to user script used in doing the analysis progress_logger: ProgressLogger object (optional) handle for logging progress shutdown_all_reloaders: bool (optional, default True) if True, all pages connected to the stats report page will be prevented from reloading after the stats report page has been completely generated **glm_kwargs: kwargs used to specify the control parameters used to specify the experimental paradigm and the GLM """ # Delayed import of nipy for more robustness when it is not present from nipy.modalities.fmri.design_matrix import DesignMatrix if slicer == "ortho": statistical_mapping_trick = False if not hasattr(cut_coords, "__iter__"): cut_coords = None # prepare for stats reporting if progress_logger is None: progress_logger = base_reporter.ProgressReport() output_dir = os.path.dirname(stats_report_filename) if not os.path.exists(output_dir): os.makedirs(output_dir) # copy css and js stuff to output dir base_reporter.copy_web_conf_files(output_dir) # initialize gallery of design matrices design_thumbs = base_reporter.ResultsGallery( loader_filename=os.path.join(output_dir, "design.html")) # initialize gallery of activation maps activation_thumbs = base_reporter.ResultsGallery( loader_filename=os.path.join(output_dir, "activation.html")) # get caller module handle from stack-frame if user_script_name is None: user_script_name = sys.argv[0] user_source_code = base_reporter.get_module_source_code(user_script_name) methods = """ GLM and Statistical Inference have been done using the <i>%s</i> script, \ powered by <a href="%s">nipy</a>. Statistic images have been thresholded at \ Z>%s voxel-level. """ % (user_script_name, base_reporter.NIPY_URL, threshold) # report the control parameters used in the paradigm and analysis design_params = "" glm_kwargs["contrasts"] = contrasts if len(glm_kwargs): design_params += ( "The following control parameters were used for " " specifying the experimental paradigm and fitting the " "GLM:<br/><ul>") design_params += base_reporter.dict_to_html_ul(glm_kwargs) if start_time is None: start_time = base_reporter.pretty_time() if title is None: title = "GLM and Statistical Inference" if not subject_id is None: title += " for subject %s" % subject_id level1_html_markup = base_reporter.get_subject_report_stats_html_template( title=title, start_time=start_time, subject_id=subject_id, # insert source code stub source_script_name=user_script_name, source_code=user_source_code, design_params=design_params, methods=methods, threshold=threshold, cmap=cmap.name) with open(stats_report_filename, 'w') as fd: fd.write(str(level1_html_markup)) fd.close() progress_logger.log("<b>Level 1 statistics</b><br/><br/>") # create design matrix thumbs if not design_matrices is None: if not hasattr(design_matrices, '__len__'): design_matrices = [design_matrices] for design_matrix, j in zip(design_matrices, xrange(len(design_matrices))): # sanitize design_matrix type if isinstance(design_matrix, basestring): if not isinstance(design_matrix, DesignMatrix): if design_matrix.endswith('.npz'): npz = np.load(design_matrix) design_matrix = DesignMatrix( npz['X'], npz['conditions'], ) else: # XXX handle case of .png, jpeg design matrix image raise TypeError("Unsupported design matrix type: %s" % type(design_matrix)) elif isinstance(design_matrix, np.ndarray) or isinstance( design_matrix, list): X = np.array(design_matrix) # assert len(X.shape) == 2 conditions = ['%i' % i for i in xrange(X.shape[-1])] design_matrix = DesignMatrix(X, conditions) # else: # raise TypeError( # "Unsupported design matrix type '%s'" % type( # design_matrix)) # plot design_matrix proper ax = design_matrix.show(rescale=True) ax.set_position([.05, .25, .9, .65]) dmat_outfile = os.path.join( output_dir, 'design_matrix_%i.png' % (j + 1), ) pl.savefig(dmat_outfile, bbox_inches="tight", dpi=200) pl.close() thumb = base_reporter.Thumbnail() thumb.a = base_reporter.a(href=os.path.basename(dmat_outfile)) thumb.img = base_reporter.img( src=os.path.basename(dmat_outfile), height="500px", ) thumb.description = "Design Matrix" thumb.description += " %s" % ( j + 1) if len(design_matrices) > 1 else "" # commit activation thumbnail into gallery design_thumbs.commit_thumbnails(thumb) # make colorbar (place-holder, will be overridden, once we've figured out # the correct end points) for activations colorbar_outfile = os.path.join(output_dir, 'activation_colorbar.png') base_reporter.make_standalone_colorbar(cmap, threshold, 8., colorbar_outfile) # create activation thumbs _vmax = 0 _vmin = threshold for contrast_id, contrast_val in contrasts.iteritems(): z_map = z_maps[contrast_id] # load the map if isinstance(z_map, basestring): z_map = nibabel.load(z_map) # compute vmin and vmax vmin, vmax = base_reporter.compute_vmin_vmax(z_map.get_data()) # update colorbar endpoints _vmax = max(_vmax, vmax) _vmin = min(_vmin, vmin) # sanitize anat if not anat is None: if anat.ndim == 4: assert anat.shape[-1] == 1, ( "Expecting 3D array for ant, got shape %s" % str(anat.shape)) anat = anat[..., 0] else: assert anat.ndim == 3, ( "Expecting 3D array for ant, got shape %s" % str(anat.shape)) # generate level 1 stats table title = "Level 1 stats for %s contrast" % contrast_id stats_table = os.path.join(output_dir, "%s_stats_table.html" % (contrast_id)) z_clusters = generate_level1_stats_table( z_map, mask, stats_table, cluster_th=cluster_th, z_threshold=threshold, title=title, ) # compute cut_coords if statistical_mapping_trick: slicer = slicer.lower() if slicer in ["x", "y", "z"] and cut_coords is None or isinstance( cut_coords, int): axis = 'xyz'.index(slicer) if cut_coords is None: cut_coords = min(5, len(z_clusters)) _cut_coords = [ x for zc in z_clusters for x in list(set(zc['maxima'][..., axis])) ] # # _cut_coords = _maximally_separated_subset( # # _cut_coords, cut_coords) # assert len(_cut_coords) == cut_coords cut_coords = _cut_coords # plot activation proper viz.plot_map(z_map.get_data(), z_map.get_affine(), cmap=cmap, anat=anat, anat_affine=anat_affine, threshold=threshold, slicer=slicer, cut_coords=cut_coords, black_bg=True) # store activation plot z_map_plot = os.path.join(output_dir, "%s_z_map.png" % contrast_id) pl.savefig(z_map_plot, dpi=200, bbox_inches='tight', facecolor="k", edgecolor="k") pl.close() # create thumbnail for activation thumbnail = base_reporter.Thumbnail() thumbnail.a = base_reporter.a(href=os.path.basename(stats_table)) thumbnail.img = base_reporter.img( src=os.path.basename(z_map_plot), height="150px", ) thumbnail.description = contrast_id activation_thumbs.commit_thumbnails(thumbnail) # make colorbar for activations base_reporter.make_standalone_colorbar(cmap, _vmin, _vmax, colorbar_outfile) # we're done, shut down re-loaders progress_logger.log('<hr/>') # prevent stats report page from reloading henceforth progress_logger.finish(stats_report_filename) # prevent any related page from reloading if shutdown_all_reloaders: progress_logger.finish_dir(output_dir) # return generated html with open(stats_report_filename, 'r') as fd: stats_report = fd.read() fd.close() return stats_report