From acea1ee14c728066864ab415559107910d168cc4 Mon Sep 17 00:00:00 2001 From: Ruge Li <91452427+rugeli@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:03:03 -0700 Subject: [PATCH] Feature/add plots to result (#255) * add mock plot data to result file * solution 1: using binary * add histogram * Co-authored-by: Megan Riel-Mehan * remove unused flag and call * wip: comment out saveResult in pack_one_seed to avoid packing twice * save each seed * merge add_histogram into histogram() * remove unused code * set save_png to true for pairwise distances * avoid early return in saving histogram --- cellpack/autopack/Analysis.py | 85 ++++++++++++------- cellpack/autopack/Environment.py | 7 +- cellpack/autopack/upy/simularium/plots.py | 36 ++++++++ .../upy/simularium/simularium_helper.py | 8 +- cellpack/autopack/writers/__init__.py | 2 + 5 files changed, 103 insertions(+), 35 deletions(-) create mode 100644 cellpack/autopack/upy/simularium/plots.py diff --git a/cellpack/autopack/Analysis.py b/cellpack/autopack/Analysis.py index 363ddd96..ddbf7343 100644 --- a/cellpack/autopack/Analysis.py +++ b/cellpack/autopack/Analysis.py @@ -54,6 +54,7 @@ def __init__( self.figures_path = self.output_path / "figures" self.figures_path.mkdir(parents=True, exist_ok=True) self.seed_to_results = {} + self.helper = autopack.helper @staticmethod def cartesian_to_sph(xyz, center=None): @@ -153,6 +154,7 @@ def plot_distance_distribution(self, all_ingredient_distances): title_str=ingr_key, x_label="pairwise distance", y_label="count", + save_png=True, ) def get_obj_dict(self, packing_results_path): @@ -586,33 +588,53 @@ def run_analysis_workflow( **analysis_config["create_report"], ) - def histogram(self, distances, filename, title_str="", x_label="", y_label=""): - plt.clf() - # calculate histogram - nbins = int(numpy.sqrt(len(distances))) - if nbins < 2: - return - y, bin_edges = numpy.histogram(distances, bins=nbins) - bincenters = 0.5 * (bin_edges[1:] + bin_edges[:-1]) - - # calculate standard error for values in each bin - bin_inds = numpy.digitize(distances, bin_edges) - x_err_vals = numpy.zeros(y.shape) - for bc in range(nbins): - dist_vals = distances[bin_inds == (bc + 1)] - if len(dist_vals) > 1: - x_err_vals[bc] = numpy.std(dist_vals) - else: - x_err_vals[bc] = 0 - y_err_vals = numpy.sqrt(y * (1 - y / numpy.sum(y))) - # set bin width - dbin = 0.9 * (bincenters[1] - bincenters[0]) - plt.bar(bincenters, y, width=dbin, color="r", xerr=x_err_vals, yerr=y_err_vals) - plt.title(title_str) - plt.xlabel(x_label) - plt.ylabel(y_label) - plt.savefig(filename) - plt.close() + def histogram( + self, + distances, + filename, + title_str="", + x_label="", + y_label="", + add_to_result=True, + save_png=False, + ): + if add_to_result: + # add histogrm to result file and display on the web page + self.helper.plot_data.add_histogram( + title=f"{title_str}: {x_label}", + xaxis_title=x_label, + traces={y_label: numpy.array(distances)}, + ) + if save_png: + # use matplotlib to create histogram and save as png + plt.clf() + # calculate histogram + nbins = int(numpy.sqrt(len(distances))) + if nbins < 2: + return + y, bin_edges = numpy.histogram(distances, bins=nbins) + bincenters = 0.5 * (bin_edges[1:] + bin_edges[:-1]) + + # calculate standard error for values in each bin + bin_inds = numpy.digitize(distances, bin_edges) + x_err_vals = numpy.zeros(y.shape) + for bc in range(nbins): + dist_vals = distances[bin_inds == (bc + 1)] + if len(dist_vals) > 1: + x_err_vals[bc] = numpy.std(dist_vals) + else: + x_err_vals[bc] = 0 + y_err_vals = numpy.sqrt(y * (1 - y / numpy.sum(y))) + # set bin width + dbin = 0.9 * (bincenters[1] - bincenters[0]) + plt.bar( + bincenters, y, width=dbin, color="r", xerr=x_err_vals, yerr=y_err_vals + ) + plt.title(title_str) + plt.xlabel(x_label) + plt.ylabel(y_label) + plt.savefig(filename) + plt.close() def plot(self, rdf, radii, file_name): plt.clf() @@ -979,7 +1001,6 @@ def pack_one_seed( seed = int(seed_list[seed_index]) seed_basename = self.env.add_seed_number_to_base_name(seed) self.env.reset() - self.env.saveResult = True numpy.random.seed(seed) self.build_grid() two_d = self.env.is_two_d() @@ -1123,7 +1144,6 @@ def pack_one_seed( ) grid_image_writer = gradient.create_voxelization(grid_image_writer) grid_image_writer.export_image() - return ( center_distance_dict, pairwise_distance_dict, @@ -1291,9 +1311,6 @@ def doloop( self.writeJSON(ingredient_occurences_file, ingredient_occurence_dict) self.writeJSON(ingredient_key_file, ingredient_key_dict) - if number_of_packings > 1: - Writer().save_as_simularium(self.env, self.seed_to_results) - all_ingredient_positions = self.combine_results_from_seeds( ingredient_position_dict ) @@ -1386,3 +1403,7 @@ def doloop( x_label="angles Z", y_label="count", ) + if number_of_packings > 1: + for seed, result in self.seed_to_results.items(): + Writer().save_as_simularium(self.env, {seed: result}) + Writer().save_as_simularium(self.env, self.seed_to_results) diff --git a/cellpack/autopack/Environment.py b/cellpack/autopack/Environment.py index b9dcd713..10bc5d19 100644 --- a/cellpack/autopack/Environment.py +++ b/cellpack/autopack/Environment.py @@ -143,7 +143,11 @@ def __init__(self, config=None, recipe=None): self.name = name self.version = recipe.get("version", "default") # saving/pickle option - self.saveResult = "out" in config + self.saveResult = ( + "out" in config + and not config["save_analyze_result"] + and not config["number_of_packings"] > 1 + ) self.out_folder = create_output_dir(config["out"], name, config["place_method"]) self.base_name = f"{self.name}_{config['name']}_{self.version}" self.grid_file_out = ( @@ -2174,7 +2178,6 @@ def pack_grid( pbar.close() self.log.info("time to fill %d", t2 - t1) all_objects = self.prep_molecules_for_save(distances, free_points, nbFreePoints) - if self.saveResult: self.save_result( free_points, diff --git a/cellpack/autopack/upy/simularium/plots.py b/cellpack/autopack/upy/simularium/plots.py new file mode 100644 index 00000000..8cd4477d --- /dev/null +++ b/cellpack/autopack/upy/simularium/plots.py @@ -0,0 +1,36 @@ +import numpy as np +from simulariumio import ScatterPlotData, HistogramPlotData + + +class PlotData: + def __init__(self): + self.plot_list = [] # list of tuples + + def _add_plot(self, plot): + self.plot_list.append(plot) + + def add_scatter(self, title, xaxis_title, yaxis_title, xtrace, ytraces): + self._add_plot( + ( + "scatter", + ScatterPlotData( + title=title, + xaxis_title=xaxis_title, + yaxis_title=yaxis_title, + xtrace=np.array(xtrace), + ytraces={key: np.array(value) for key, value in ytraces.items()}, + ), + ) + ) + + def add_histogram(self, title, xaxis_title, traces): + self._add_plot( + ( + "histogram", + HistogramPlotData( + title=title, + xaxis_title=xaxis_title, + traces={key: np.array(value) for key, value in traces.items()}, + ), + ) + ) diff --git a/cellpack/autopack/upy/simularium/simularium_helper.py b/cellpack/autopack/upy/simularium/simularium_helper.py index 457e0c85..a87ad5b0 100644 --- a/cellpack/autopack/upy/simularium/simularium_helper.py +++ b/cellpack/autopack/upy/simularium/simularium_helper.py @@ -22,6 +22,7 @@ from simulariumio.constants import DISPLAY_TYPE, VIZ_TYPE from cellpack.autopack.upy import hostHelper +from cellpack.autopack.upy.simularium.plots import PlotData from cellpack.autopack.DBRecipeHandler import DBUploader, DBMaintenance from cellpack.autopack.interface_objects.database_ids import DATABASE_IDS import collada @@ -112,6 +113,7 @@ def __init__(self, master=None, vi=None): self.nogui = True self.hext = "dae" self.max_fiber_length = 0 + self.plot_data = PlotData() @staticmethod def format_rgb_color(color): @@ -1370,7 +1372,11 @@ def writeToFile(self, file_name, bb, recipe_name, version): time_units=UnitData("ns"), # nanoseconds spatial_units=UnitData("nm"), # nanometers ) - TrajectoryConverter(converted_data).save(file_name, False) + converter = TrajectoryConverter(converted_data) + plot_list = self.plot_data.plot_list + for type, plot in plot_list: + converter.add_plot(plot, type) + converter.save(file_name, False) return file_name def raycast(self, **kw): diff --git a/cellpack/autopack/writers/__init__.py b/cellpack/autopack/writers/__init__.py index 1d1b57ce..5b439ecb 100644 --- a/cellpack/autopack/writers/__init__.py +++ b/cellpack/autopack/writers/__init__.py @@ -188,6 +188,8 @@ def save_as_simularium(self, env, seed_to_results_map): is_aggregate = len(seed_to_results_map) > 1 if is_aggregate: result_file_name = f"{env.result_file.split('_seed')[0]}_all" + else: + result_file_name = f"{env.result_file.split('_seed')[0]}_seed_{list(seed_to_results_map.keys())[0]}" file_name = env.helper.writeToFile( result_file_name, env.boundingBox, env.name, env.version )