diff --git a/Docs/ChangeLog.md b/Docs/ChangeLog.md
index a565514..405538b 100644
--- a/Docs/ChangeLog.md
+++ b/Docs/ChangeLog.md
@@ -1,8 +1,14 @@
-# Version: 1.116.1
-## Date: 2024-10-28
+# Version: 1.117.1
+## Date: 2024-11-5
### Changes:
-- Fix: Fixed the bug of when plot the heatmap of taxa-funcs with t-ststistic and f-statistic, the value still selected as p-value.
-- Change: Updated the cookbook.
+- New: 1. Added an option to calculate the mean by the domainat value in eache group for plot_mean parameter in DEV Settings. 2. Added a statistics bar plot for basic plot part. 3. Added a attributes table of Taxa-Function result.
+- Fix: Fixed the bug of calculating the Functional Redundancy in the T-Test and ANOVA part.
+
+
+# Version: 1.117.0
+## Date: 2024-10-31
+### Changes:
+- New: added result Table after plotting the network of Taxa-Functions and Co-Expression.
# Version: 1.116.0
## Date: 2024-10-18
diff --git a/README.md b/README.md
index e432a94..c329c93 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ The desktop version comes fully set up and ready to use, including all required
### `Command-line version`:
-Clone the repository to your local machine and install the required dependencies.
+Use [PyPi](https://pypi.org/project/MetaXTools/) to install, then type `metax` in the terminal to launch the GUI.
```bash
python -m pip install MetaXTools
```
@@ -45,7 +45,7 @@ Clone the repository to your local machine and install the required dependencies
## Getting Started
- `Desktop Version(desktop)`:
- - Refer to the MetaX Cookbook for detailed instructions on how to use MetaX wtih the graphical user interface.
+ - Refer to the MetaX Cookbook for detailed instructions on how to use MetaX with the graphical user interface.
- `Command-line version`:
- Read the example documentation in the [Notebook](https://github.com/byemaxx/MetaX/blob/main/Docs/example.ipynb) for detailed instructions and examples.
diff --git a/metax/gui/main_gui.py b/metax/gui/main_gui.py
index 5feea8c..bf0bdf5 100644
--- a/metax/gui/main_gui.py
+++ b/metax/gui/main_gui.py
@@ -67,7 +67,7 @@
from metax.taxafunc_ploter.volcano_plot_js import VolcanoPlotJS
from metax.taxafunc_ploter.volcano_plot import VolcanoPlot
from metax.taxafunc_ploter.tukey_plot import TukeyPlot
- from metax.taxafunc_ploter.bar_plot_js import BarPlot_js
+ from metax.taxafunc_ploter.bar_plot_js import BarPlot
from metax.taxafunc_ploter.sankey_plot import SankeyPlot
from metax.taxafunc_ploter.network_plot import NetworkPlot
from metax.taxafunc_ploter.trends_plot import TrendsPlot
@@ -110,7 +110,7 @@
from ..taxafunc_ploter.volcano_plot_js import VolcanoPlotJS
from ..taxafunc_ploter.volcano_plot import VolcanoPlot
from ..taxafunc_ploter.tukey_plot import TukeyPlot
- from ..taxafunc_ploter.bar_plot_js import BarPlot_js
+ from ..taxafunc_ploter.bar_plot_js import BarPlot
from ..taxafunc_ploter.sankey_plot import SankeyPlot
from ..taxafunc_ploter.network_plot import NetworkPlot
from ..taxafunc_ploter.trends_plot import TrendsPlot
@@ -733,6 +733,7 @@ def show_settings_window(self):
settings_widget.heatmap_params_dict_changed.connect(self.on_heatmap_params_changed)
settings_widget.tf_link_net_params_dict_changed.connect(self.on_tf_link_net_params_changed)
settings_widget.html_theme_changed.connect(self.on_html_theme_changed)
+ settings_widget.stat_mean_by_zero_dominant_changed.connect(self.on_stat_mean_by_zero_dominant_changed)
# Other settings
settings_widget.protein_infer_method_changed.connect(self.on_protein_infer_method_changed)
@@ -764,6 +765,15 @@ def on_tf_link_net_params_changed(self, params_dict):
def on_html_theme_changed(self, theme):
self.html_theme = theme
print(f"HTML theme changed to: {theme}")
+
+ def on_stat_mean_by_zero_dominant_changed(self, mode):
+ # chcek if self.tfa exists
+ if not hasattr(self, 'tfa'):
+ print("Please load the data first.")
+ return
+
+ self.tfa.stat_mean_by_zero_dominant = mode
+ print(f"Stat mean by zero dominant changed to: {mode}")
def on_protein_infer_method_changed(self, method):
#save to settings
@@ -3639,9 +3649,8 @@ def plot_basic_list(self, plot_type='heatmap'):
plot_percent = self.checkBox_basic_bar_plot_percent.isChecked()
sub_meta = self.comboBox_3dbar_sub_meta.currentText()
use_3d_for_sub_meta = self.checkBox_basic_bar_3d_for_sub_meta.isChecked()
+ js_bar = self.checkBox_basic_bar_interactive_js.isChecked()
- width = width*100
- height = height*100
df = df.loc[(df!=0).any(axis=1)]
if len(df) > 100:
reply = QMessageBox.question(self.MainWindow, 'Warning',
@@ -3650,14 +3659,23 @@ def plot_basic_list(self, plot_type='heatmap'):
if reply == QMessageBox.No:
return None
self.show_message(f'Plotting {plot_type}...')
- pic = BarPlot_js(self.tfa, theme=self.html_theme).plot_intensity_bar(df = df, width=width, height=height,
- title= '', rename_taxa=rename_taxa,
- show_legend=show_legend, font_size=font_size,
- rename_sample=rename_sample, plot_mean = plot_mean,
- plot_percent = plot_percent, sub_meta = sub_meta,
- show_all_labels = show_all_labels, use_3d = use_3d_for_sub_meta)
-
- self.save_and_show_js_plot(pic, title)
+ if js_bar:
+ width = width*100
+ height = height*100
+ pic = BarPlot(self.tfa, theme=self.html_theme).plot_intensity_bar_js(df = df, width=width, height=height,
+ title= '', rename_taxa=rename_taxa,
+ show_legend=show_legend, font_size=font_size,
+ rename_sample=rename_sample, plot_mean = plot_mean,
+ plot_percent = plot_percent, sub_meta = sub_meta,
+ show_all_labels = show_all_labels, use_3d = use_3d_for_sub_meta)
+
+ self.save_and_show_js_plot(pic, title)
+ else:
+ ax = BarPlot(self.tfa, theme=self.html_theme).plot_intensity_bar_sns(df = df, width=width, height=height,
+ title= '', rename_taxa=rename_taxa,
+ show_legend=show_legend, font_size=font_size,
+ rename_sample=rename_sample, plot_mean = plot_mean,
+ plot_percent = plot_percent, sub_meta = sub_meta)
elif plot_type == 'get_table':
self.show_message('Getting table...')
@@ -5170,15 +5188,19 @@ def plot_co_expr(self, plot_type = 'network'):
elif plot_type == 'network':
try:
self.show_message('Co-expression network is plotting...\n\n It may take a long time! Please wait...')
- pic = NetworkPlot(self.tfa,
+ pic, corr_df = NetworkPlot(self.tfa,
show_labels=show_labels,
rename_taxa=rename_taxa,
font_size=font_size,
theme=self.html_theme,
**self.tf_link_net_params_dict
).plot_co_expression_network(df_type= df_type, corr_method=corr_method,
- corr_threshold=corr_threshold, sample_list=sample_list, width=width, height=height, focus_list=focus_list, plot_list_only=plot_list_only)
+ corr_threshold=corr_threshold, sample_list=sample_list,
+ width=width, height=height, focus_list=focus_list,
+ plot_list_only=plot_list_only)
self.save_and_show_js_plot(pic, 'co-expression network')
+ self.update_table_dict(f'co-expression_network({df_type})', corr_df)
+
except ValueError as e:
if 'sample_list should have at least 2' in str(e):
QMessageBox.warning(self.MainWindow, 'Error', "At least 2 samples are required!")
@@ -5409,7 +5431,7 @@ def plot_network(self):
try:
self.show_message('Plotting network...')
list_only_no_link = self.checkBox_tf_link_net_plot_list_only_no_link.isChecked()
- pic = NetworkPlot(
+ pic, network_df, attributes_df = NetworkPlot(
self.tfa,
show_labels=show_labels,
rename_taxa=rename_taxa,
@@ -5425,6 +5447,9 @@ def plot_network(self):
list_only_no_link=list_only_no_link,
)
self.save_and_show_js_plot(pic, 'taxa-func link Network')
+ self.update_table_dict('taxa-func_network', network_df)
+ self.update_table_dict('taxa-func_network_attributes', attributes_df)
+
except Exception as e:
error_message = traceback.format_exc()
self.logger.write_log(f'plot_network error: {error_message}', 'e')
@@ -5676,7 +5701,7 @@ def plot_tflink_bar(self):
params['sub_meta'] = sub_meta
self.show_message('Plotting bar plot, please wait...')
- pic = BarPlot_js(self.tfa, theme=self.html_theme).plot_intensity_bar(**params)
+ pic = BarPlot(self.tfa, theme=self.html_theme).plot_intensity_bar_js(**params)
self.save_and_show_js_plot(pic, 'Intensity Bar Plot')
diff --git a/metax/gui/metax_gui/main_window.ui b/metax/gui/metax_gui/main_window.ui
index 81c7523..c6e3adb 100644
--- a/metax/gui/metax_gui/main_window.ui
+++ b/metax/gui/metax_gui/main_window.ui
@@ -7,7 +7,7 @@
0
0
1122
- 755
+ 816
@@ -245,8 +245,8 @@
0
0
- 528
- 534
+ 391
+ 80
@@ -1882,7 +1882,7 @@
Show Labels
- true
+ false
@@ -2789,8 +2789,8 @@
0
0
- 621
- 150
+ 1016
+ 184
@@ -3004,22 +3004,6 @@
- -
-
-
-
- 0
- 0
-
-
-
- Show Legend
-
-
- true
-
-
-
-
@@ -3280,6 +3264,36 @@
+ -
+
+
-
+
+
+ Interactive Bar
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Show Legend
+
+
+ true
+
+
+
+
+
@@ -3792,7 +3806,7 @@
0
0
- 1020
+ 878
128
@@ -7467,8 +7481,8 @@
0
0
- 1016
- 105
+ 620
+ 65
@@ -9288,8 +9302,8 @@
0
0
- 1016
- 141
+ 383
+ 68
@@ -9785,8 +9799,8 @@
0
0
- 1044
- 493
+ 313
+ 41
diff --git a/metax/gui/metax_gui/setting_window.ui b/metax/gui/metax_gui/setting_window.ui
index 653cab3..3851027 100644
--- a/metax/gui/metax_gui/setting_window.ui
+++ b/metax/gui/metax_gui/setting_window.ui
@@ -78,14 +78,14 @@
Plotting
- -
-
+
-
+
- HTML Global
+ Taxa-Functions Link Network
- -
+
-
-
@@ -325,32 +325,7 @@
- -
-
-
-
-
-
- Theme
-
-
-
- -
-
-
-
-
- white
-
-
- -
-
- dark
-
-
-
-
-
-
- -
+
-
-
@@ -516,10 +491,10 @@
- -
-
+
-
+
- Taxa-Functions Link Network
+ HTML Global
@@ -699,13 +674,45 @@
- -
+
-
Network Global
+ -
+
+
+ Plot Mean
+
+
+
+ -
+
+
-
+
+
+ Theme
+
+
+
+ -
+
+
-
+
+ white
+
+
+ -
+
+ dark
+
+
+
+
+
+
-
@@ -713,6 +720,17 @@
+ -
+
+
-
+
+
+ Calculate Non-Zero Mean (Return 0 if Zeros > 50%) for Each Group
+
+
+
+
+
diff --git a/metax/gui/metax_gui/settings_widget.py b/metax/gui/metax_gui/settings_widget.py
index ba2b3cc..53ebfb7 100644
--- a/metax/gui/metax_gui/settings_widget.py
+++ b/metax/gui/metax_gui/settings_widget.py
@@ -9,6 +9,7 @@ class SettingsWidget(QWidget):
tf_link_net_params_dict_changed = pyqtSignal(dict)
html_theme_changed = pyqtSignal(str)
protein_infer_method_changed = pyqtSignal(str)
+ stat_mean_by_zero_dominant_changed = pyqtSignal(bool)
def __init__(self, parent=None, update_branch="main", auto_check_update=True, QSettings=None):
super().__init__(parent)
@@ -65,6 +66,9 @@ def __init__(self, parent=None, update_branch="main", auto_check_update=True, QS
# Protein inference method
self.ui.comboBox_protein_infer_greedy_mode.currentTextChanged.connect(self.handle_protein_infer_method_changed)
+ # stat_mean_by_zero_dominant
+ self.ui.checkBox_stat_mean_by_zero_dominant.stateChanged.connect(self.handle_stat_mean_by_zero_dominant_changed)
+
def init_ui(self, update_mode, auto_check_update, QSettings=None):
if update_mode == "main":
@@ -142,6 +146,10 @@ def handle_protein_infer_method_changed(self):
'fast': 'heap',
}
self.protein_infer_method_changed.emit(method[protein_infer_greedy_mode])
+
+ def handle_stat_mean_by_zero_dominant_changed(self):
+ checked = self.ui.checkBox_stat_mean_by_zero_dominant.isChecked()
+ self.stat_mean_by_zero_dominant_changed.emit(checked)
if __name__ == "__main__":
import sys
diff --git a/metax/gui/metax_gui/ui_main_window.py b/metax/gui/metax_gui/ui_main_window.py
index 64b4e2f..0e713a3 100644
--- a/metax/gui/metax_gui/ui_main_window.py
+++ b/metax/gui/metax_gui/ui_main_window.py
@@ -14,7 +14,7 @@
class Ui_metaX_main(object):
def setupUi(self, metaX_main):
metaX_main.setObjectName("metaX_main")
- metaX_main.resize(1122, 755)
+ metaX_main.resize(1122, 816)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@@ -147,7 +147,7 @@ def setupUi(self, metaX_main):
self.toolBox_2.setMaximumSize(QtCore.QSize(1677, 16777215))
self.toolBox_2.setObjectName("toolBox_2")
self.page_2 = QtWidgets.QWidget()
- self.page_2.setGeometry(QtCore.QRect(0, 0, 528, 534))
+ self.page_2.setGeometry(QtCore.QRect(0, 0, 391, 80))
self.page_2.setObjectName("page_2")
self.gridLayout_27 = QtWidgets.QGridLayout(self.page_2)
self.gridLayout_27.setObjectName("gridLayout_27")
@@ -909,7 +909,7 @@ def setupUi(self, metaX_main):
sizePolicy.setHeightForWidth(self.checkBox_pca_if_show_lable.sizePolicy().hasHeightForWidth())
self.checkBox_pca_if_show_lable.setSizePolicy(sizePolicy)
self.checkBox_pca_if_show_lable.setLayoutDirection(QtCore.Qt.LeftToRight)
- self.checkBox_pca_if_show_lable.setChecked(True)
+ self.checkBox_pca_if_show_lable.setChecked(False)
self.checkBox_pca_if_show_lable.setObjectName("checkBox_pca_if_show_lable")
self.horizontalLayout_34.addWidget(self.checkBox_pca_if_show_lable)
self.gridLayout_34.addLayout(self.horizontalLayout_34, 1, 2, 1, 1)
@@ -1411,7 +1411,7 @@ def setupUi(self, metaX_main):
self.scrollArea_2.setWidgetResizable(True)
self.scrollArea_2.setObjectName("scrollArea_2")
self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
- self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 621, 150))
+ self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 1016, 184))
self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
self.gridLayout_50 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_2)
self.gridLayout_50.setObjectName("gridLayout_50")
@@ -1532,15 +1532,6 @@ def setupUi(self, metaX_main):
self.label_183.setFont(font)
self.label_183.setObjectName("label_183")
self.gridLayout_70.addWidget(self.label_183, 0, 0, 1, 1)
- self.checkBox_basic_bar_show_legend = QtWidgets.QCheckBox(self.scrollAreaWidgetContents_2)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.checkBox_basic_bar_show_legend.sizePolicy().hasHeightForWidth())
- self.checkBox_basic_bar_show_legend.setSizePolicy(sizePolicy)
- self.checkBox_basic_bar_show_legend.setChecked(True)
- self.checkBox_basic_bar_show_legend.setObjectName("checkBox_basic_bar_show_legend")
- self.gridLayout_70.addWidget(self.checkBox_basic_bar_show_legend, 4, 1, 1, 1)
self.checkBox_basic_heatmap_sankey_title = QtWidgets.QCheckBox(self.scrollAreaWidgetContents_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@@ -1683,6 +1674,22 @@ def setupUi(self, metaX_main):
self.checkBox_basic_bar_3d_for_sub_meta = QtWidgets.QCheckBox(self.scrollAreaWidgetContents_2)
self.checkBox_basic_bar_3d_for_sub_meta.setObjectName("checkBox_basic_bar_3d_for_sub_meta")
self.gridLayout_70.addWidget(self.checkBox_basic_bar_3d_for_sub_meta, 4, 3, 1, 1)
+ self.horizontalLayout_104 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_104.setObjectName("horizontalLayout_104")
+ self.checkBox_basic_bar_interactive_js = QtWidgets.QCheckBox(self.scrollAreaWidgetContents_2)
+ self.checkBox_basic_bar_interactive_js.setChecked(True)
+ self.checkBox_basic_bar_interactive_js.setObjectName("checkBox_basic_bar_interactive_js")
+ self.horizontalLayout_104.addWidget(self.checkBox_basic_bar_interactive_js)
+ self.checkBox_basic_bar_show_legend = QtWidgets.QCheckBox(self.scrollAreaWidgetContents_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.checkBox_basic_bar_show_legend.sizePolicy().hasHeightForWidth())
+ self.checkBox_basic_bar_show_legend.setSizePolicy(sizePolicy)
+ self.checkBox_basic_bar_show_legend.setChecked(True)
+ self.checkBox_basic_bar_show_legend.setObjectName("checkBox_basic_bar_show_legend")
+ self.horizontalLayout_104.addWidget(self.checkBox_basic_bar_show_legend)
+ self.gridLayout_70.addLayout(self.horizontalLayout_104, 4, 1, 1, 1)
self.gridLayout_50.addLayout(self.gridLayout_70, 0, 0, 1, 1)
self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2)
self.gridLayout_41.addWidget(self.scrollArea_2, 0, 0, 1, 1)
@@ -1960,7 +1967,7 @@ def setupUi(self, metaX_main):
self.scrollArea_cross_heatmap_settings.setWidgetResizable(True)
self.scrollArea_cross_heatmap_settings.setObjectName("scrollArea_cross_heatmap_settings")
self.scrollAreaWidgetContents_3 = QtWidgets.QWidget()
- self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 1020, 128))
+ self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 878, 128))
self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3")
self.gridLayout_38 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_3)
self.gridLayout_38.setObjectName("gridLayout_38")
@@ -3840,7 +3847,7 @@ def setupUi(self, metaX_main):
self.scrollArea_5.setWidgetResizable(True)
self.scrollArea_5.setObjectName("scrollArea_5")
self.scrollAreaWidgetContents_6 = QtWidgets.QWidget()
- self.scrollAreaWidgetContents_6.setGeometry(QtCore.QRect(0, 0, 1016, 105))
+ self.scrollAreaWidgetContents_6.setGeometry(QtCore.QRect(0, 0, 620, 65))
self.scrollAreaWidgetContents_6.setObjectName("scrollAreaWidgetContents_6")
self.gridLayout_57 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_6)
self.gridLayout_57.setObjectName("gridLayout_57")
@@ -4849,7 +4856,7 @@ def setupUi(self, metaX_main):
self.scrollArea_7.setWidgetResizable(True)
self.scrollArea_7.setObjectName("scrollArea_7")
self.scrollAreaWidgetContents_8 = QtWidgets.QWidget()
- self.scrollAreaWidgetContents_8.setGeometry(QtCore.QRect(0, 0, 1016, 141))
+ self.scrollAreaWidgetContents_8.setGeometry(QtCore.QRect(0, 0, 383, 68))
self.scrollAreaWidgetContents_8.setObjectName("scrollAreaWidgetContents_8")
self.gridLayout_66 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_8)
self.gridLayout_66.setObjectName("gridLayout_66")
@@ -5096,7 +5103,7 @@ def setupUi(self, metaX_main):
self.toolBox_metalab_res_anno = QtWidgets.QToolBox(self.tab_18)
self.toolBox_metalab_res_anno.setObjectName("toolBox_metalab_res_anno")
self.page_3 = QtWidgets.QWidget()
- self.page_3.setGeometry(QtCore.QRect(0, 0, 1044, 493))
+ self.page_3.setGeometry(QtCore.QRect(0, 0, 313, 41))
self.page_3.setObjectName("page_3")
self.gridLayout_45 = QtWidgets.QGridLayout(self.page_3)
self.gridLayout_45.setObjectName("gridLayout_45")
@@ -5779,7 +5786,6 @@ def retranslateUi(self, metaX_main):
self.label_13.setText(_translate("metaX_main", "Theme"))
self.checkBox_basic_hetatmap_row_cluster.setText(_translate("metaX_main", "Row Cluster"))
self.label_183.setText(_translate("metaX_main", "General"))
- self.checkBox_basic_bar_show_legend.setText(_translate("metaX_main", "Show Legend"))
self.checkBox_basic_heatmap_sankey_title.setText(_translate("metaX_main", "Show Title"))
self.label_184.setText(_translate("metaX_main", "Heatmap"))
self.checkBox_basic_hetatmap_col_cluster.setText(_translate("metaX_main", "Col Cluster"))
@@ -5796,6 +5802,8 @@ def retranslateUi(self, metaX_main):
self.checkBox_basic_hetatmap_rename_taxa.setText(_translate("metaX_main", "Taxa"))
self.label_108.setText(_translate("metaX_main", "Label Font Size"))
self.checkBox_basic_bar_3d_for_sub_meta.setText(_translate("metaX_main", "3D for Sub Meta"))
+ self.checkBox_basic_bar_interactive_js.setText(_translate("metaX_main", "Interactive Bar"))
+ self.checkBox_basic_bar_show_legend.setText(_translate("metaX_main", "Show Legend"))
self.label_34.setText(_translate("metaX_main", "List for Plotting"))
self.pushButton_basic_heatmap_add_top.setToolTip(_translate("metaX_main", "Add conditionally filtered items to the drawing box"))
self.pushButton_basic_heatmap_add_top.setText(_translate("metaX_main", "Add Top to List"))
diff --git a/metax/gui/metax_gui/ui_setting_window.py b/metax/gui/metax_gui/ui_setting_window.py
index 245df5c..a9108fb 100644
--- a/metax/gui/metax_gui/ui_setting_window.py
+++ b/metax/gui/metax_gui/ui_setting_window.py
@@ -44,9 +44,9 @@ def setupUi(self, Settings):
self.page_2.setObjectName("page_2")
self.gridLayout_4 = QtWidgets.QGridLayout(self.page_2)
self.gridLayout_4.setObjectName("gridLayout_4")
- self.label_21 = QtWidgets.QLabel(self.page_2)
- self.label_21.setObjectName("label_21")
- self.gridLayout_4.addWidget(self.label_21, 2, 0, 1, 1)
+ self.label_4 = QtWidgets.QLabel(self.page_2)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout_4.addWidget(self.label_4, 5, 0, 1, 1)
self.gridLayout_7 = QtWidgets.QGridLayout()
self.gridLayout_7.setObjectName("gridLayout_7")
self.label_11 = QtWidgets.QLabel(self.page_2)
@@ -149,18 +149,7 @@ def setupUi(self, Settings):
self.doubleSpinBox_tf_link_net_gravity.setProperty("value", 0.2)
self.doubleSpinBox_tf_link_net_gravity.setObjectName("doubleSpinBox_tf_link_net_gravity")
self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_gravity, 1, 3, 1, 1)
- self.gridLayout_4.addLayout(self.gridLayout_7, 3, 3, 1, 1)
- self.gridLayout_5 = QtWidgets.QGridLayout()
- self.gridLayout_5.setObjectName("gridLayout_5")
- self.label_22 = QtWidgets.QLabel(self.page_2)
- self.label_22.setObjectName("label_22")
- self.gridLayout_5.addWidget(self.label_22, 0, 0, 1, 1)
- self.comboBox_html_theme = QtWidgets.QComboBox(self.page_2)
- self.comboBox_html_theme.setObjectName("comboBox_html_theme")
- self.comboBox_html_theme.addItem("")
- self.comboBox_html_theme.addItem("")
- self.gridLayout_5.addWidget(self.comboBox_html_theme, 0, 1, 1, 1)
- self.gridLayout_4.addLayout(self.gridLayout_5, 2, 3, 1, 1)
+ self.gridLayout_4.addLayout(self.gridLayout_7, 4, 3, 1, 1)
self.gridLayout_6 = QtWidgets.QGridLayout()
self.gridLayout_6.setObjectName("gridLayout_6")
self.label_5 = QtWidgets.QLabel(self.page_2)
@@ -219,10 +208,10 @@ def setupUi(self, Settings):
self.lineEdit_tf_link_net_line_color = QtWidgets.QLineEdit(self.page_2)
self.lineEdit_tf_link_net_line_color.setObjectName("lineEdit_tf_link_net_line_color")
self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_line_color, 2, 1, 1, 1)
- self.gridLayout_4.addLayout(self.gridLayout_6, 4, 3, 1, 1)
- self.label_4 = QtWidgets.QLabel(self.page_2)
- self.label_4.setObjectName("label_4")
- self.gridLayout_4.addWidget(self.label_4, 4, 0, 1, 1)
+ self.gridLayout_4.addLayout(self.gridLayout_6, 5, 3, 1, 1)
+ self.label_21 = QtWidgets.QLabel(self.page_2)
+ self.label_21.setObjectName("label_21")
+ self.gridLayout_4.addWidget(self.label_21, 3, 0, 1, 1)
self.gridLayout_8 = QtWidgets.QGridLayout()
self.gridLayout_8.setObjectName("gridLayout_8")
self.label_2 = QtWidgets.QLabel(self.page_2)
@@ -288,10 +277,30 @@ def setupUi(self, Settings):
self.gridLayout_4.addLayout(self.gridLayout_8, 1, 3, 1, 1)
self.label_20 = QtWidgets.QLabel(self.page_2)
self.label_20.setObjectName("label_20")
- self.gridLayout_4.addWidget(self.label_20, 3, 0, 1, 1)
+ self.gridLayout_4.addWidget(self.label_20, 4, 0, 1, 1)
+ self.label_27 = QtWidgets.QLabel(self.page_2)
+ self.label_27.setObjectName("label_27")
+ self.gridLayout_4.addWidget(self.label_27, 2, 0, 1, 1)
+ self.gridLayout_5 = QtWidgets.QGridLayout()
+ self.gridLayout_5.setObjectName("gridLayout_5")
+ self.label_22 = QtWidgets.QLabel(self.page_2)
+ self.label_22.setObjectName("label_22")
+ self.gridLayout_5.addWidget(self.label_22, 0, 0, 1, 1)
+ self.comboBox_html_theme = QtWidgets.QComboBox(self.page_2)
+ self.comboBox_html_theme.setObjectName("comboBox_html_theme")
+ self.comboBox_html_theme.addItem("")
+ self.comboBox_html_theme.addItem("")
+ self.gridLayout_5.addWidget(self.comboBox_html_theme, 0, 1, 1, 1)
+ self.gridLayout_4.addLayout(self.gridLayout_5, 3, 3, 1, 1)
self.label = QtWidgets.QLabel(self.page_2)
self.label.setObjectName("label")
self.gridLayout_4.addWidget(self.label, 1, 0, 1, 1)
+ self.gridLayout_12 = QtWidgets.QGridLayout()
+ self.gridLayout_12.setObjectName("gridLayout_12")
+ self.checkBox_stat_mean_by_zero_dominant = QtWidgets.QCheckBox(self.page_2)
+ self.checkBox_stat_mean_by_zero_dominant.setObjectName("checkBox_stat_mean_by_zero_dominant")
+ self.gridLayout_12.addWidget(self.checkBox_stat_mean_by_zero_dominant, 0, 0, 1, 1)
+ self.gridLayout_4.addLayout(self.gridLayout_12, 2, 3, 1, 1)
self.toolBox.addItem(self.page_2, "")
self.page_3 = QtWidgets.QWidget()
self.page_3.setGeometry(QtCore.QRect(0, 0, 748, 340))
@@ -326,7 +335,7 @@ def retranslateUi(self, Settings):
self.radioButton_update_stable.setText(_translate("Settings", "Stable"))
self.radioButton_update_beta.setText(_translate("Settings", "Beta"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page), _translate("Settings", "General"))
- self.label_21.setText(_translate("Settings", "HTML Global"))
+ self.label_4.setText(_translate("Settings", "Taxa-Functions Link Network"))
self.label_11.setText(_translate("Settings", "Line Width"))
self.label_15.setToolTip(_translate("Settings", "The larger the value the greater the repulsion"))
self.label_15.setText(_translate("Settings", "Repulsion"))
@@ -348,9 +357,6 @@ def retranslateUi(self, Settings):
self.comboBox_tf_link_net_label_position.setItemText(6, _translate("Settings", "insideRight"))
self.label_19.setToolTip(_translate("Settings", "The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre."))
self.label_19.setText(_translate("Settings", "Gravity"))
- self.label_22.setText(_translate("Settings", "Theme"))
- self.comboBox_html_theme.setItemText(0, _translate("Settings", "white"))
- self.comboBox_html_theme.setItemText(1, _translate("Settings", "dark"))
self.label_5.setText(_translate("Settings", "Taxa Shape"))
self.lineEdit_tf_link_net_func_focus_color.setText(_translate("Settings", "#B24745"))
self.lineEdit_tf_link_net_taxa_focus_color.setText(_translate("Settings", "#6A6599"))
@@ -377,7 +383,7 @@ def retranslateUi(self, Settings):
self.comboBox_tf_link_net_taxa_sahpe.setItemText(6, _translate("Settings", "arrow"))
self.label_13.setText(_translate("Settings", "Line Color"))
self.lineEdit_tf_link_net_line_color.setText(_translate("Settings", "#9aa7b1"))
- self.label_4.setText(_translate("Settings", "Taxa-Functions Link Network"))
+ self.label_21.setText(_translate("Settings", "HTML Global"))
self.label_2.setText(_translate("Settings", "Linkage Method"))
self.comboBox_heatmap_linkage_method.setCurrentText(_translate("Settings", "average"))
self.comboBox_heatmap_linkage_method.setItemText(0, _translate("Settings", "average"))
@@ -401,7 +407,12 @@ def retranslateUi(self, Settings):
self.label_25.setText(_translate("Settings", "X Labels Rotation"))
self.label_26.setText(_translate("Settings", "Y Labels Rotation"))
self.label_20.setText(_translate("Settings", "Network Global"))
+ self.label_27.setText(_translate("Settings", "Plot Mean"))
+ self.label_22.setText(_translate("Settings", "Theme"))
+ self.comboBox_html_theme.setItemText(0, _translate("Settings", "white"))
+ self.comboBox_html_theme.setItemText(1, _translate("Settings", "dark"))
self.label.setText(_translate("Settings", "HeatMap"))
+ self.checkBox_stat_mean_by_zero_dominant.setText(_translate("Settings", "Calculate Non-Zero Mean (Return 0 if Zeros > 50%) for Each Group"))
self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("Settings", "Plotting"))
self.label_23.setText(_translate("Settings", "Protein Infer"))
self.comboBox_protein_infer_greedy_mode.setItemText(0, _translate("Settings", "fast"))
diff --git a/metax/taxafunc_analyzer/analyzer.py b/metax/taxafunc_analyzer/analyzer.py
index 62536a4..10cbe13 100644
--- a/metax/taxafunc_analyzer/analyzer.py
+++ b/metax/taxafunc_analyzer/analyzer.py
@@ -83,6 +83,8 @@ def __init__(
self.split_func_status:bool = False
self.split_func_sep:str = ''
+
+ self.stat_mean_by_zero_dominant:bool = False # only for BasicStats.get_stats_mean_df_by_group()
# load function
self.BasicStats = BasicStats(self)
diff --git a/metax/taxafunc_analyzer/analyzer_utils/basic_stats.py b/metax/taxafunc_analyzer/analyzer_utils/basic_stats.py
index d79f70b..5772024 100644
--- a/metax/taxafunc_analyzer/analyzer_utils/basic_stats.py
+++ b/metax/taxafunc_analyzer/analyzer_utils/basic_stats.py
@@ -7,7 +7,46 @@ def __init__(self, tfa):
self.tfa = tfa
# get a mean df by group
- def get_stats_mean_df_by_group(self, df: pd.DataFrame = None, condition: list = None) -> pd.DataFrame:
+ def get_stats_mean_df_by_group(self, df: pd.DataFrame, condition: list|None = None, zero_dominant: bool|None = None) -> pd.DataFrame:
+ """
+ Calculate the mean values of groups of samples in a DataFrame, optionally considering only non-zero values.
+
+ Args:
+ df (pd.DataFrame): The input DataFrame containing the sample data. Defaults to None.
+ condition (list, optional): A list of conditions to filter the samples. Defaults to None.eg. ['V1', 'PBS']
+ zero_domainant (bool, optional): If True, calculate the mean of non-zero values in each group if the number of zero values is less than half of the total number of values; otherwise, return 0. Defaults to False.
+
+ Returns:
+ pd.DataFrame: A DataFrame containing the mean values of the groups.
+ """
+
+ def get_mean_by_zero_dominant(df: pd.DataFrame) -> pd.Series:
+ """
+ Optimized function to calculate the mean of non-zero values in each row if the number of zero values
+ is less than half of the total values; otherwise, return 0.
+
+ Args:
+ df (pd.DataFrame): Input DataFrame.
+
+ Returns:
+ pd.Series: A Series with mean values based on the zero-dominant condition.
+ """
+ # 计算每行的零值数量
+ zero_counts = (df == 0).sum(axis=1)
+ # 判断每行零值是否超过一半,超过的行直接设为0
+ mean_series = pd.Series(0, index=df.index)
+ non_zero_rows = zero_counts <= (df.shape[1] / 2)
+ # 对非零主导的行计算非零均值
+ mean_series[non_zero_rows] = df[non_zero_rows].replace(0, pd.NA).mean(axis=1, skipna=True)
+ return mean_series
+
+ if zero_dominant is None:
+ zero_dominant = self.tfa.stat_mean_by_zero_dominant
+ print(f"Caculating mean by zero_dominant: [{zero_dominant}]")
+
+ mean_method = get_mean_by_zero_dominant if zero_dominant else lambda x: x.mean(axis=1)
+
+
data = df.copy()
# extract samples that are in the data only
columns_list = data.columns.tolist()
@@ -18,6 +57,7 @@ def get_stats_mean_df_by_group(self, df: pd.DataFrame = None, condition: list =
group_order = list(OrderedDict.fromkeys(self.tfa.get_group_of_a_sample(sample) for sample in data.columns))
print("input group order:", group_order)
+
samples_used =[]
group_means = pd.DataFrame()
for group in group_order:
@@ -30,11 +70,14 @@ def get_stats_mean_df_by_group(self, df: pd.DataFrame = None, condition: list =
samples_used.extend(valid_samples)
group_data = data[valid_samples]
# calculate the mean of the samples in the group
- group_mean = group_data.mean(axis=1)
+ group_mean = mean_method(group_data)
+
# add the group mean to the group_means dataframe
group_means[group] = group_mean
group_means = group_means[group_order]
- print("samples used:", samples_used)
+ # convert to float
+ group_means = group_means.astype(float)
+ # print("samples used:", samples_used)
return group_means
def get_stats_peptide_num_in_taxa(self) -> pd.DataFrame:
diff --git a/metax/taxafunc_analyzer/analyzer_utils/cross_test.py b/metax/taxafunc_analyzer/analyzer_utils/cross_test.py
index 6ae5e51..ecc9b9b 100644
--- a/metax/taxafunc_analyzer/analyzer_utils/cross_test.py
+++ b/metax/taxafunc_analyzer/analyzer_utils/cross_test.py
@@ -631,18 +631,28 @@ def get_stats_diff_taxa_but_func(self, group_list: list|None = None, p_value: fl
if p_value < 0 or p_value > 1:
raise ValueError("p_value must be between 0 and 1")
# 获取pvalue大于0.05的Taxon items
- not_significant_taxa = df_taxa_test_res[df_taxa_test_res[p_col_name] >= p_value].index.get_level_values('Taxon').tolist()
+ not_significant_taxa_list = df_taxa_test_res[df_taxa_test_res[p_col_name] >= p_value].index.get_level_values('Taxon').tolist()
+ significant_taxa_list = df_taxa_test_res[df_taxa_test_res[p_col_name] < p_value].index.get_level_values('Taxon').tolist()
print(f"Under {p_col_name} = {p_value}: \n \
- Significant Taxa: [{len(df_taxa_test_res) - len(not_significant_taxa)}], Not Significant Taxa: [{len(not_significant_taxa)}]")
+ Significant Taxa: [{len(significant_taxa_list)}], Not Significant Taxa: [{len(not_significant_taxa_list)}]")
# 获取pvalue小于0.05的Function items
- not_significant_func = df_func_test_res[df_func_test_res[p_col_name] >= p_value].index.get_level_values(self.tfa.func_name).tolist()
+ not_significant_func_list = df_func_test_res[df_func_test_res[p_col_name] >= p_value].index.get_level_values(self.tfa.func_name).tolist()
+ significant_func_list = df_func_test_res[df_func_test_res[p_col_name] < p_value].index.get_level_values(self.tfa.func_name).tolist()
print(f"Under {p_col_name} = {p_value}: \n \
- Significant Function: [{len(df_func_test_res) - len(not_significant_func)}], Not Significant Function: [{len(not_significant_func)}]")
+ Significant Function: [{len(significant_func_list)}], Not Significant Function: [{len(not_significant_func_list)}]")
# 选择这些Taxon在df_taxa_func_test_res中的行 and pvalue < 0.05
- df_filtered_taxa_not_significant = df_taxa_func_test_res.loc[df_taxa_func_test_res.index.get_level_values('Taxon').isin(not_significant_taxa) & (df_taxa_func_test_res[p_col_name] < p_value)]
+ df_filtered_taxa_not_significant = df_taxa_func_test_res.loc[
+ df_taxa_func_test_res.index.get_level_values('Taxon').isin(not_significant_taxa_list) &
+ (df_taxa_func_test_res[p_col_name] < p_value) &
+ (df_taxa_func_test_res.index.get_level_values(self.tfa.func_name).isin(significant_func_list))
+ ]
print(f"Taxa not significant but related function significant with {p_col_name} < {p_value}: [{len(df_filtered_taxa_not_significant)}]")
- df_filtered_func_not_significant = df_taxa_func_test_res.loc[df_taxa_func_test_res.index.get_level_values(self.tfa.func_name).isin(not_significant_func) & (df_taxa_func_test_res[p_col_name] < p_value)]
+ df_filtered_func_not_significant = df_taxa_func_test_res.loc[
+ df_taxa_func_test_res.index.get_level_values(self.tfa.func_name).isin(not_significant_func_list) &
+ (df_taxa_func_test_res[p_col_name] < p_value) &
+ (df_taxa_func_test_res.index.get_level_values('Taxon').isin(significant_taxa_list))
+ ]
# reset_index for df_filtered_func_not_significant
df_filtered_func_not_significant = df_filtered_func_not_significant.swaplevel(0, 1).sort_index()
print(f"Function not significant but related taxa significant with {p_col_name} < {p_value}: [{len(df_filtered_func_not_significant)}]")
diff --git a/metax/taxafunc_ploter/bar_plot_js.py b/metax/taxafunc_ploter/bar_plot_js.py
index 5be6694..fddfafb 100644
--- a/metax/taxafunc_ploter/bar_plot_js.py
+++ b/metax/taxafunc_ploter/bar_plot_js.py
@@ -1,9 +1,10 @@
from pyecharts import options as opts
from pyecharts.charts import Bar
from .get_distinct_colors import GetDistinctColors
+from matplotlib import pyplot as plt
-class BarPlot_js:
+class BarPlot:
def __init__(self, tfobj, theme='white'):
self.tfa = tfobj
self.get_distinct_colors = GetDistinctColors().get_distinct_colors
@@ -41,18 +42,20 @@ def _add_group_name_to_sample(self, df):
df.columns = new_col_names
return df
-
- def plot_intensity_bar(self, taxon_name:str|None=None, sample_list:list|None = None,
- func_name:str|None =None, peptide_seq=None,
- width:int=1200, height:int=800, df= None,
- title:str|None =None, rename_taxa:bool=False,
- show_legend:bool=True, font_size:int=10,
- rename_sample:bool=True, plot_mean:bool=False,
- plot_percent:bool=False, sub_meta:str="None",
- show_all_labels:tuple = (False, False), use_3d:bool=False
- ):
+ def _get_modfied_df(self, df= None,
+ taxon_name:str|None=None, sample_list:list|None = None,
+ func_name:str|None =None, peptide_seq=None,
+ rename_taxa:bool=False, rename_sample:bool=True,
+ plot_mean:bool=False, sub_meta:str="None", plot_percent:bool=False):
+ """
+ Get modified df for plotting
+ Returns:
+ - df: pd.DataFrame
+ - rename_sample: bool
+ """
if df is None:
- df = self.tfa.GetMatrix.get_intensity_matrix(taxon_name=taxon_name, func_name=func_name, peptide_seq=peptide_seq, sample_list= sample_list)
+ df = self.tfa.GetMatrix.get_intensity_matrix(taxon_name=taxon_name, func_name=func_name,
+ peptide_seq=peptide_seq, sample_list= sample_list)
if df.empty:
raise ValueError('No data to plot')
@@ -63,25 +66,69 @@ def plot_intensity_bar(self, taxon_name:str|None=None, sample_list:list|None = N
df = self.tfa.BasicStats.get_stats_mean_df_by_group(df)
rename_sample = False
-
- if use_3d:
- if sub_meta == "None":
- use_3d = False
- print('sub_meta must be provided for 3D bar plot, use 2D bar plot instead')
-
+
# rename taxa
if rename_taxa:
df = self.rename_taxa(df)
#rename columns (sample name)
if rename_sample and sub_meta == "None":
df = self._add_group_name_to_sample(df)
-
- col_num = len(df)
-
+
if plot_percent:
# transform to percentage of each column
df = df.div(df.sum(axis=0), axis=1) * 100
+ return df, rename_sample
+
+
+ def plot_intensity_bar_sns(self, taxon_name:str|None=None, sample_list:list|None = None,
+ func_name:str|None =None, peptide_seq=None,
+ width:int=12, height:int=8, df= None,
+ title:str|None =None, rename_taxa:bool=False,
+ show_legend:bool=True, font_size:int=10,
+ rename_sample:bool=True, plot_mean:bool=False,
+ plot_percent:bool=False, sub_meta:str="None", **kwargs):
+
+ df, _ = self._get_modfied_df(df, taxon_name, sample_list, func_name, peptide_seq,
+ rename_taxa, rename_sample, plot_mean, sub_meta, plot_percent)
+ colormap = GetDistinctColors().get_distinct_colors(len(df.index))
+ fig, ax = plt.subplots(figsize=(width, height))
+ df.T.plot(kind='bar', stacked=True, ax=ax, color=colormap)
+
+ ax.set_title(title, fontsize=font_size)
+ ax.set_xlabel('Group' if plot_mean else 'Sample', fontsize=font_size)
+ ax.set_ylabel('Percentage' if plot_percent else 'Intensity', fontsize=font_size)
+ ax.tick_params(axis='both', which='major', labelsize=font_size)
+ if show_legend:
+ ax.legend(title='Taxa' if rename_taxa else 'Sample', title_fontsize=font_size, fontsize=font_size, loc='center left', bbox_to_anchor=(1, 0.5))
+ else:
+ ax.get_legend().remove()
+
+ plt.tight_layout()
+ plt.show()
+
+ return fig
+
+
+
+ def plot_intensity_bar_js(self, taxon_name:str|None=None, sample_list:list|None = None,
+ func_name:str|None =None, peptide_seq=None,
+ width:int=1200, height:int=800, df= None,
+ title:str|None =None, rename_taxa:bool=False,
+ show_legend:bool=True, font_size:int=10,
+ rename_sample:bool=True, plot_mean:bool=False,
+ plot_percent:bool=False, sub_meta:str="None",
+ show_all_labels:tuple = (False, False), use_3d:bool=False
+ ):
+ df, rename_sample = self._get_modfied_df(df, taxon_name, sample_list, func_name, peptide_seq,
+ rename_taxa, rename_sample, plot_mean, sub_meta, plot_percent)
+
+ if use_3d:
+ if sub_meta == "None":
+ use_3d = False
+ print('sub_meta must be provided for 3D bar plot, use 2D bar plot instead')
+
+ col_num = len(df)
# Create title
if title is None:
diff --git a/metax/taxafunc_ploter/heatmap_plot.py b/metax/taxafunc_ploter/heatmap_plot.py
index 5190664..0cdbd21 100644
--- a/metax/taxafunc_ploter/heatmap_plot.py
+++ b/metax/taxafunc_ploter/heatmap_plot.py
@@ -357,10 +357,17 @@ def plot_basic_heatmap(self, df, title = 'Heatmap',fig_size:tuple|None = None,
'''
if plot_mean and sub_meta == 'None': # if sub_meta is not None, plot_mean is False
- df = self.tfa.BasicStats.get_stats_mean_df_by_group(df)
print('Plot the mean of the data, set rename_sample to False')
rename_sample = False
+ df = self.tfa.BasicStats.get_stats_mean_df_by_group(df)
+ # remove all 0 rows
+ row_num = len(df)
+ df = df.loc[~(df==0).all(axis=1)] if row_cluster else df
+ # remove all 0 columns
+ col_num = len(df.columns)
+ df = df.loc[:, (df != 0).any(axis=0)] if col_cluster else df
+ print(f"Remove all 0 rows and columns after calculating the mean of the data:\n{row_num - len(df)} rows are removed\n{col_num - len(df.columns)} columns are removed")
if len(df) < 2:
row_cluster = False
diff --git a/metax/taxafunc_ploter/network_plot.py b/metax/taxafunc_ploter/network_plot.py
index 655930d..f857861 100644
--- a/metax/taxafunc_ploter/network_plot.py
+++ b/metax/taxafunc_ploter/network_plot.py
@@ -1,6 +1,8 @@
from pyecharts import options as opts
from pyecharts.charts import Graph
import pandas as pd
+import numpy as np
+from typing import Tuple
class NetworkPlot:
def __init__(self, tfobj,
@@ -60,7 +62,7 @@ def __init__(self, tfobj,
self.show_sub_title = show_sub_title
- def modify_focus_list(self, focus_list):
+ def modify_focus_list(self, focus_list, return_taxa_functions_separately = True):
'''
Split the taxa-func item into taxa and function if it's in the focus_list
'''
@@ -71,8 +73,12 @@ def modify_focus_list(self, focus_list):
taxa = i.split(' <')[0].split('|')[-1]
func = i.split(' <')[1][:-1]
# i = taxa.split('|')[-1] + ' <' + func + '>'
- new_focus_list.append(taxa)
- new_focus_list.append(func)
+ if return_taxa_functions_separately:
+ new_focus_list.append(taxa)
+ new_focus_list.append(func)
+ else:
+ taxa_func = taxa + ' <' + func + '>'
+ new_focus_list.append(taxa_func)
else: # taxa item
i = i.split('|')[-1]
new_focus_list.append(i)
@@ -99,12 +105,13 @@ def create_nodes_links(
- sample_list (list, optional): Specifies which samples to include. If None, all samples are used.
- focus_list (list, optional): List of taxa and functions to highlight in the network.
- plot_list_only (bool, optional): If True, only items and theri linked items in focus_list are plotted.
- - strict_list (bool, optional): If True, only items in focus_list are plotted.
+ - list_only_no_link (bool, optional): If True, only items in focus_list are plotted, not including the links of the focus items.
Returns:
- nodes (list): Information about each node for the graph, including name and size.
- links (list): Information about links between nodes.
- categories (list): Categories for nodes, used for coloring in the graph.
+ - cytoscape_df (DataFrame): DataFrame containing nodes and links for Cytoscape export.
"""
df = self.tfa.taxa_func_df.copy()
if self.rename_taxa:
@@ -120,13 +127,46 @@ def create_nodes_links(
print("No sample list provided, using all samples")
df = df.loc[~(df==0).all(axis=1)]
- df['sum'] = df.sum(axis=1)
+
+ # create network_df for export to cytoscape
+ network_df = self.tfa.BasicStats.get_stats_mean_df_by_group(df)
+ network_df.reset_index(inplace=True)
+ network_df.columns = ['taxa', 'function'] + network_df.columns.tolist()[2:]
+ taxa_dict = network_df.drop('function', axis=1).groupby('taxa').sum().to_dict()
+ func_dict = network_df.drop('taxa', axis=1).groupby('function').sum().to_dict()
+ network_df['focus_taxa'] = network_df['taxa'].apply(lambda x: 'Y' if x in focus_list else 'N')
+ network_df['focus_func'] = network_df['function'].apply(lambda x: 'Y' if x in focus_list else 'N')
+ # cerate attributes_df
+ attributes_taxa_df = pd.DataFrame(network_df[['taxa']])
+ attributes_taxa_df.drop_duplicates(inplace=True)
+ attributes_taxa_df['focus'] = attributes_taxa_df['taxa'].apply(lambda x: 'Y' if x in focus_list else 'N')
+ attributes_taxa_df.columns = ['node', 'focus']
+ attributes_taxa_df['type'] = 'taxa'
+ # add the intensity columns to the attributes_df
+ for col in taxa_dict.keys():
+ attributes_taxa_df[col] = attributes_taxa_df['node'].map(taxa_dict[col])
+
+ attributes_func_df = pd.DataFrame(network_df[['function']])
+ attributes_func_df.drop_duplicates(inplace=True)
+ attributes_func_df['focus'] = attributes_func_df['function'].apply(lambda x: 'Y' if x in focus_list else 'N')
+ attributes_func_df.columns = ['node', 'focus']
+ attributes_func_df['type'] = 'function'
+ # add the intensity columns to the attributes_df
+ for col in func_dict.keys():
+ attributes_func_df[col] = attributes_func_df['node'].map(func_dict[col])
+
+ # concatenate the taxa and function attributes_df
+ attributes_df = pd.concat([attributes_taxa_df, attributes_func_df])
+ attributes_df['mean'] = attributes_df.drop(['node', 'focus', 'type'], axis=1).mean(axis=1)
+ # Done creating network_df and attributes_df for export to cytoscape
+
+ df['mean'] = df.mean(axis=1)
df.reset_index(inplace=True)
colname = df.columns.tolist()
colname[0] = 'taxa'
colname[1] = 'function'
df.columns = colname
- df = df[['taxa', 'function', 'sum']]
+ df = df[['taxa', 'function', 'mean']]
if plot_list_only:
print("Plotting only the list provided in focus_list")
@@ -143,14 +183,13 @@ def create_nodes_links(
df_func['taxa'] = ""
# concatenate the uncovered taxa and functions
df = pd.concat([df_coverd, df_taxa, df_func])
-
-
+
else:
df = df.loc[df['taxa'].isin(focus_list) | df['function'].isin(focus_list)]
print(f"New df shape: {df.shape}")
- taxa_sum = df[df['taxa'] != ""].groupby('taxa')['sum'].sum().to_dict()
- function_sum = df[df['function'] != ""].groupby('function')['sum'].sum().to_dict()
+ taxa_sum = df[df['taxa'] != ""].groupby('taxa')['mean'].sum().to_dict()
+ function_sum = df[df['function'] != ""].groupby('function')['mean'].sum().to_dict()
sum_dict = {**taxa_sum, **function_sum}
min_value = min(sum_dict.values())
@@ -190,7 +229,6 @@ def normalize(value):
{"name": "Focus_Function", "itemStyle": {"normal": {"color": self.func_focus_color}}},
]
- return nodes, links, categories
else:
nodes = [{"name": taxon, "category": 0, "symbolSize": normalize(taxa_sum[taxon]), "value": taxa_sum[taxon], "symbol": self.taxa_shape} for taxon in taxa] + \
@@ -202,7 +240,7 @@ def normalize(value):
{"name": "Function", "itemStyle": {"normal": {"color": self.func_color}}},
]
- return nodes, links, categories
+ return nodes, links, categories, network_df, attributes_df
def plot_tflink_network(
self,
@@ -212,7 +250,7 @@ def plot_tflink_network(
focus_list: list = None,
plot_list_only: bool = False,
list_only_no_link: bool = False,
- ):
+ ) -> Tuple[Graph, pd.DataFrame]:
"""
Creates a network graph of taxa and functions using Pyecharts.
@@ -224,10 +262,12 @@ def plot_tflink_network(
- height (int, optional): Height of the graph in pixels.
- focus_list (list, optional): List of taxa and functions to highlight.
- plot_list_only (bool, optional): If True, only plots items in focus_list and their linked items.
- - strict_list_only (bool, optional): If True, only plots items in focus_list.
+ - list_only_no_link (bool, optional): If True, only plots items in focus_list, not including the links of the focus items.
Returns:
- A Pyecharts Graph object that can be displayed in Jupyter notebooks or web pages.
+ - A DataFrame containing nodes and links for export to Cytoscape.
+ - A DataFrame containing attributes of the nodes for export to Cytoscape.
"""
# preprocess focus_list
@@ -244,10 +284,13 @@ def plot_tflink_network(
new_list.extend((taxon, func))
else:
print(f"Warning: {i} is not in taxa or function list")
- nodes, links, categories = self.create_nodes_links(sample_list, new_list,plot_list_only, list_only_no_link)
+ nodes, links, categories, network_df, attributes_df = self.create_nodes_links(sample_list=sample_list,
+ focus_list = new_list,
+ plot_list_only = plot_list_only,
+ list_only_no_link=list_only_no_link)
else:
focus_list = []
- nodes, links, categories = self.create_nodes_links(sample_list)
+ nodes, links, categories, network_df, attributes_df = self.create_nodes_links(sample_list = sample_list)
c = (
@@ -317,13 +360,20 @@ def plot_tflink_network(
)
- return c
+ return c , network_df, attributes_df
def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pearson',
corr_threshold:float=0.5, sample_list:list[str]|None = None,
width:int = 12, height:int = 8, focus_list:list[str] = [], plot_list_only:bool = False,
- ):
+ ) -> Tuple[Graph, pd.DataFrame]:
+ """
+ Plots a co-expression network based on the correlation matrix of the specified data type.
+ Returns:
+ --------
+ Tuple[Graph, pd.DataFrame]
+ A tuple containing the network plot (Graph) and the network data frame (pd.DataFrame).
+ """
from matplotlib import colormaps
#check sample_list length
if sample_list and len(sample_list) < 2:
@@ -341,7 +391,7 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea
print("Renaming taxa to last level")
df = self.tfa.rename_taxa(df)
# modify the focus_list to the last level taxa
- focus_list = self.modify_focus_list(focus_list)
+ focus_list = self.modify_focus_list(focus_list, return_taxa_functions_separately=False)
if extra_cols := sample_list:
print(f"Using sample list provided {extra_cols}")
@@ -358,6 +408,18 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea
correlation_matrix = df.corr(method='spearman')
else:
raise ValueError(f"corr_method should be pearson or spearman, but got {corr_method}")
+
+ # cerate network_df for export to cytoscape from the correlation matrix
+ mask = np.triu(np.ones(correlation_matrix.shape), k=1).astype(bool)
+ network_df = network_df = correlation_matrix.where(mask)
+ # set index name and header name as item1 and item2
+ network_df.index.name = f'{df_type}1'
+ network_df.columns.name = f'{df_type}2'
+ network_df = network_df.stack().reset_index()
+ network_df.columns = [ f'{df_type}1', f'{df_type}2', 'correlation']
+ network_df['item1_focus'] = network_df[f'{df_type}1'].apply(lambda x: 'Y' if x in focus_list else 'N')
+ network_df['item2_focus'] = network_df[f'{df_type}2'].apply(lambda x: 'Y' if x in focus_list else 'N')
+ # Done creating network_df
node_sizes = correlation_matrix.apply(lambda x: (x > corr_threshold).sum(), axis=1)
max_node_size = node_sizes.max()
@@ -491,7 +553,7 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea
),
)
)
- return pic
+ return pic, network_df
diff --git a/metax/utils/version.py b/metax/utils/version.py
index d936c4f..f987022 100644
--- a/metax/utils/version.py
+++ b/metax/utils/version.py
@@ -1,2 +1,2 @@
-__version__ = '1.116.1'
+__version__ = '1.117.1'
API_version = '3'
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 209f360..b89d208 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "MetaXTools"
-version = "1.116.1"
+version = "1.117.1"
description = "MetaXTools is a novel tool for linking peptide sequences with taxonomic and functional information in Metaproteomics."
readme = "README_PyPi.md"
license = { text = "NorthOmics" }