From 923b0b1e8dccced5a5155e0868a082539cc3cd02 Mon Sep 17 00:00:00 2001 From: Qing <44231502+byemaxx@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:28:03 -0400 Subject: [PATCH] - Fix: Fixed the bug when restore the object, some cross_test top table was added to the GUI by wrong. - Change: Changed the "pixel_ratio" from 2 to 3 for the HTML plot to make the plot more clear. --- Docs/ChangeLog.md | 7 + metax/gui/main_gui.py | 2 +- metax/taxafunc_ploter/bar_plot_js.py | 4 +- metax/taxafunc_ploter/network_plot.py | 4 +- metax/taxafunc_ploter/sankey_plot.py | 4 +- metax/taxafunc_ploter/sunburst_plot.py | 2 +- .../test_scripts/universal_sankey.py | 167 ++++++++++++++++++ metax/taxafunc_ploter/treemap_plot.py | 2 +- metax/taxafunc_ploter/trends_plot_js.py | 2 +- metax/taxafunc_ploter/volcano_plot_js.py | 2 +- metax/utils/version.py | 2 +- 11 files changed, 186 insertions(+), 12 deletions(-) create mode 100644 metax/taxafunc_ploter/test_scripts/universal_sankey.py diff --git a/Docs/ChangeLog.md b/Docs/ChangeLog.md index 1cb7e38..5e010f3 100644 --- a/Docs/ChangeLog.md +++ b/Docs/ChangeLog.md @@ -1,3 +1,10 @@ +# Version: 1.109.9 +## Date: 2024-08-7 +### Changes: +- Fix: Fixed the bug when restore the object, some cross_test top table was added to the GUI by wrong. +- Change: Changed the "pixel_ratio" from 2 to 3 for the HTML plot to make the plot more clear. + + # Version: 1.109.8 ## Date: 2024-08-1 ### Changes: diff --git a/metax/gui/main_gui.py b/metax/gui/main_gui.py index 5ab5f22..f418f61 100644 --- a/metax/gui/main_gui.py +++ b/metax/gui/main_gui.py @@ -1199,7 +1199,7 @@ def restore_table_names_to_combox_after_load_taxafunc_obj(self): # checek if name is a part of current_table_name for name in current_table_name_list: - if any([match in name for match in top_heatmap_match_list]): + if any([match in name for match in top_heatmap_match_list]) and 'Cross_Test[' not in name: comboBox_top_heatmap_table_list.append(name) elif 'deseq2(' in name: comboBox_deseq2_tables_list.append(name) diff --git a/metax/taxafunc_ploter/bar_plot_js.py b/metax/taxafunc_ploter/bar_plot_js.py index 7c41c1b..b04e374 100644 --- a/metax/taxafunc_ploter/bar_plot_js.py +++ b/metax/taxafunc_ploter/bar_plot_js.py @@ -126,7 +126,7 @@ def plot_intensity_bar(self, taxon_name:str=None, sample_list:list = None, save_as_image=opts.ToolBoxFeatureSaveAsImageOpts( type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG", ), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), @@ -259,7 +259,7 @@ def plot_intensity_bar(self, taxon_name:str=None, sample_list:list = None, feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG"), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), data_zoom=opts.ToolBoxFeatureDataZoomOpts(zoom_title="Zoom", diff --git a/metax/taxafunc_ploter/network_plot.py b/metax/taxafunc_ploter/network_plot.py index 92a6349..655930d 100644 --- a/metax/taxafunc_ploter/network_plot.py +++ b/metax/taxafunc_ploter/network_plot.py @@ -296,7 +296,7 @@ def plot_tflink_network( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts( type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG", ), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), @@ -472,7 +472,7 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea save_as_image=opts.ToolBoxFeatureSaveAsImageOpts( type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG", ), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), diff --git a/metax/taxafunc_ploter/sankey_plot.py b/metax/taxafunc_ploter/sankey_plot.py index e54f247..9913ca9 100644 --- a/metax/taxafunc_ploter/sankey_plot.py +++ b/metax/taxafunc_ploter/sankey_plot.py @@ -231,7 +231,7 @@ def __plot_sankey(self,link_nodes_dict, width, height, title, subtitle=''): nodes=nodes, links=links, node_align='justify', - layout_iterations=50, + layout_iterations=100, node_width=25, emphasis_opts=opts.EmphasisOpts(focus='adjacency'), linestyle_opt=opts.LineStyleOpts( @@ -252,7 +252,7 @@ def __plot_sankey(self,link_nodes_dict, width, height, title, subtitle=''): feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG"), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), data_zoom=opts.ToolBoxFeatureDataZoomOpts(zoom_title="Zoom", diff --git a/metax/taxafunc_ploter/sunburst_plot.py b/metax/taxafunc_ploter/sunburst_plot.py index 9dcd37e..b78137e 100644 --- a/metax/taxafunc_ploter/sunburst_plot.py +++ b/metax/taxafunc_ploter/sunburst_plot.py @@ -149,7 +149,7 @@ def create_sunburst_chart(self, taxa_df, width=10, height=8, title='Sunburst', s feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG"), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), data_view=opts.ToolBoxFeatureDataViewOpts(title="Data View"), diff --git a/metax/taxafunc_ploter/test_scripts/universal_sankey.py b/metax/taxafunc_ploter/test_scripts/universal_sankey.py new file mode 100644 index 0000000..0ac30b0 --- /dev/null +++ b/metax/taxafunc_ploter/test_scripts/universal_sankey.py @@ -0,0 +1,167 @@ +import pandas as pd +from pyecharts.charts import Sankey +from pyecharts import options as opts + +class SankeyPlot: + def __init__(self, theme='white'): + self.font_size = 12 + self.show_legend = True + self.theme = theme + + def df_to_sankey_df(self, df, value_col='value'): + df.index.name = 'index' + df['index'] = df.index + df = df[['index', value_col]] + + if '<' in df['index'][0]: + index_str = df['index'].str.split("<", expand=True) + taxon_index, func_index = (0, 1) if '|' in index_str[0][0] else (1, 0) + df = df[[value_col]] + df['Taxon'] = index_str[taxon_index].str.replace(">", "") + df['Function'] = index_str[func_index].str.replace(">", "") + else: + df = df[[value_col]] + df['Taxon'] = df.index + + df_t = df['Taxon'].str.split('|', expand=True) + if "Function" in df.columns: + df_t = df_t.join(df['Function']) + + df_t = df_t.join(df[value_col]) + names = df_t.columns.tolist() + names[-1] = 'value' + df_t.columns = names + df_t = df_t[df_t['value'] != 0] + + return df_t + + def convert_df_by_group_for_sankey(self, df, plot_mean=False): + sample_list = df.columns.tolist() + + if plot_mean is not None: + if plot_mean is None: + df = df.mean(axis=1).to_frame(name='mean') + + group_dict = {col: col for col in df.columns} + else: + group_dict = {sample: sample for sample in sample_list} + + df_dict = {} + if len(sample_list) > 1: + df['sum'] = df.sum(axis=1) + df_dict['All'] = self.df_to_sankey_df(df, value_col='sum') + + for group, samples in group_dict.items(): + df_temp = df[samples] + if isinstance(df_temp, pd.Series): + df_temp = pd.DataFrame(df_temp) + + df_temp['sum'] = df_temp.sum(axis=1) + df_temp = df_temp[df_temp['sum'] != 0] + df_temp = self.df_to_sankey_df(df_temp, value_col='sum') + df_dict[group] = df_temp + + return df_dict + + def create_nodes_links(self, df, value_col='value'): + lis = df.columns.tolist()[:-1] + lis1 = lis[:-1] + lis2 = lis[1:] + + df2 = pd.DataFrame() + for i in zip(lis1, lis2): + dfi = df.pivot_table(value_col, index=list(i), aggfunc='sum').reset_index() + dfi.columns = [0, 1, 2] + df2 = pd.concat([df2, dfi]) + + nodes = [] + ln = df2.iloc[:, 0].to_list() + df2.iloc[:, 1].to_list() + ln = list(set(ln)) + for i in ln: + dic = {'name': i} + nodes.append(dic) + print(f'Number of nodes: {len(nodes)}') + + links = [] + for i in df2.values: + dic = {'source': i[0], 'target': i[1], 'value': i[2]} + links.append(dic) + print(f'Number of links: {len(links)}') + + colors = GetDistinctColors().get_distinct_colors(20, convert=True) + node_colors = {} + for idx, node in enumerate(nodes): + available_colors = colors[:] + for link in links: + if link['source'] == node['name'] and link['target'] in node_colors: + if node_colors[link['target']] in available_colors: + available_colors.remove(node_colors[link['target']]) + if link['target'] == node['name'] and link['source'] in node_colors: + if node_colors[link['source']] in available_colors: + available_colors.remove(node_colors[link['source']]) + if not available_colors: + available_colors = colors[:] + chosen_color = available_colors[idx % len(available_colors)] + node_colors[node['name']] = chosen_color + node['itemStyle'] = {'color': chosen_color} + + return nodes, links + + def __plot_sankey(self, link_nodes_dict, width, height, title, subtitle=''): + pic = Sankey(init_opts=opts.InitOpts(width=f"{width*100}px", height=f"{height*100}px", theme=self.theme)) + + for key, value in link_nodes_dict.items(): + nodes = value[0] + links = value[1] + num = value[2] + pic.add( + f'{key} ({num})', + nodes=nodes, + links=links, + node_align='justify', + layout_iterations=100, + node_width=25, + emphasis_opts=opts.EmphasisOpts(focus='adjacency'), + linestyle_opt=opts.LineStyleOpts(curve=0.5, opacity=0.3, color="gray"), + label_opts=opts.LabelOpts(position='right', font_size=self.font_size, color='white' if self.theme == 'dark' else 'black'), + itemstyle_opts=opts.ItemStyleOpts(border_width=1, border_color="black", opacity=0.7), + ) + + pic.set_global_opts( + legend_opts=opts.LegendOpts(selected_mode='single', is_show=self.show_legend, type_="scroll", page_icon_size=8), + toolbox_opts=opts.ToolboxOpts( + is_show=True, + orient="vertical", + pos_left="left", + pos_top="bottom", + feature=opts.ToolBoxFeatureOpts( + save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="png", background_color="black" if self.theme == 'dark' else "white", pixel_ratio=3, title="Save as PNG"), + restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), + data_zoom=opts.ToolBoxFeatureDataZoomOpts(zoom_title="Zoom", is_show=False, back_title="Back"), + data_view=opts.ToolBoxFeatureDataViewOpts(title="Data View"), + magic_type=opts.ToolBoxFeatureMagicTypeOpts(line_title="Line", bar_title="Bar", is_show=False, stack_title="Stack", tiled_title="Tiled"), + ), + ), + title_opts=opts.TitleOpts(title=title, subtitle=subtitle, title_textstyle_opts=opts.TextStyleOpts(font_size=self.font_size + 2)), + ) + + return pic + + def plot_intensity_sankey(self, df, width=12, height=8, title="Sankey Plot", subtitle="", font_size=12, show_legend=True, plot_mean=False): + df = df.copy() + self.font_size = font_size + self.show_legend = show_legend + + df_sankey = self.convert_df_by_group_for_sankey(df, plot_mean=plot_mean) + link_nodes_dict = {} + for key, value in df_sankey.items(): + print(f'Creating nodes and links for {key}...') + nodes, links = self.create_nodes_links(value) + link_nodes_dict[key] = [nodes, links, len(value.index)] + pic = self.__plot_sankey(link_nodes_dict, width=width, height=height, title=title, subtitle=subtitle) + return pic + +# df_sankey is a dataframe, index is "taxon" or "taxon < function>", columns are values +df_sankey = pd.read_csv('data.csv') +pic = SankeyPlot().plot_intensity_sankey(df_sankey, width=12, height=8, title="Sankey Plot", subtitle="", font_size=12, show_legend=False, plot_mean=False) +pic.render_notebook() \ No newline at end of file diff --git a/metax/taxafunc_ploter/treemap_plot.py b/metax/taxafunc_ploter/treemap_plot.py index 0fa67d0..91c70a4 100644 --- a/metax/taxafunc_ploter/treemap_plot.py +++ b/metax/taxafunc_ploter/treemap_plot.py @@ -140,7 +140,7 @@ def create_treemap_chart(self, taxa_df, width=10, height=8, title='TreeMap', sho feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG"), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), data_view=opts.ToolBoxFeatureDataViewOpts(title="Data View"), diff --git a/metax/taxafunc_ploter/trends_plot_js.py b/metax/taxafunc_ploter/trends_plot_js.py index 9376597..86f951a 100644 --- a/metax/taxafunc_ploter/trends_plot_js.py +++ b/metax/taxafunc_ploter/trends_plot_js.py @@ -97,7 +97,7 @@ def plot_trends_js(self, df, width:int=15000, height:int=500, title:str|None = N feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG"), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), data_view=opts.ToolBoxFeatureDataViewOpts(title="Data View"), diff --git a/metax/taxafunc_ploter/volcano_plot_js.py b/metax/taxafunc_ploter/volcano_plot_js.py index dcefee9..413e43b 100644 --- a/metax/taxafunc_ploter/volcano_plot_js.py +++ b/metax/taxafunc_ploter/volcano_plot_js.py @@ -82,7 +82,7 @@ def color_mapping(type_value): feature=opts.ToolBoxFeatureOpts( save_as_image=opts.ToolBoxFeatureSaveAsImageOpts(type_="png", background_color="black" if self.theme == 'dark' else "white", - pixel_ratio=2, + pixel_ratio=3, title="Save as PNG"), restore=opts.ToolBoxFeatureRestoreOpts(title="Restore"), data_view=opts.ToolBoxFeatureDataViewOpts(title="Data View"), diff --git a/metax/utils/version.py b/metax/utils/version.py index 57a4ae9..6afbf81 100644 --- a/metax/utils/version.py +++ b/metax/utils/version.py @@ -1,2 +1,2 @@ -__version__ = '1.109.8' +__version__ = '1.109.9' API_version = '2' \ No newline at end of file