From a46d9cdb5ba9d970661220fe6469e14c7fc68d87 Mon Sep 17 00:00:00 2001 From: erusg Date: Thu, 1 Jun 2023 10:06:17 -0400 Subject: [PATCH] Add harmonization plugin --- NiChartGUI/mainwindow.py | 12 +- NiChartGUI/plugins/harmonizeview/__init__.py | 0 .../plugins/harmonizeview/harmonizeview.py | 202 +++++++++++++ .../plugins/harmonizeview/harmonizeview.ui | 285 ++++++++++++++++++ .../harmonizeview/harmonizeview.yapsy-plugin | 15 + NiChartGUI/plugins/spareview/spareview.py | 6 +- .../plugins/spareview/spareview.yapsy-plugin | 2 +- pyproject.toml | 4 +- requirements.txt | 2 +- testing/tmp_test1.sh | 10 + 10 files changed, 531 insertions(+), 7 deletions(-) create mode 100644 NiChartGUI/plugins/harmonizeview/__init__.py create mode 100644 NiChartGUI/plugins/harmonizeview/harmonizeview.py create mode 100644 NiChartGUI/plugins/harmonizeview/harmonizeview.ui create mode 100644 NiChartGUI/plugins/harmonizeview/harmonizeview.yapsy-plugin create mode 100644 testing/tmp_test1.sh diff --git a/NiChartGUI/mainwindow.py b/NiChartGUI/mainwindow.py index 70dbb75..bdd611c 100644 --- a/NiChartGUI/mainwindow.py +++ b/NiChartGUI/mainwindow.py @@ -73,12 +73,22 @@ def __init__(self, dataFiles = None, dictFiles = None): ## Sort plugins based on tab position. Create a dictionary of plugins indSort = np.argsort(plIndTmp) + + ## FIXME Select to activate few plugins: + ## ['Dataset View', 'Filter View', 'Dist View', 'Plot View', 'Merge View', 'Normalize View', 'Adjust Cov View', 'Harmonize View', 'Spare View'] + indSort = indSort[[0,3,5,6,7,8]] + plTmp = np.array(plTmp)[indSort] plNameTmp = np.array(plNameTmp)[indSort] + self.pluginDescriptions = np.array(pluginDescriptions)[indSort] # A list with plugin descriptions self.Plugins = dict(zip(plNameTmp, plTmp)) # A dictionary with plugin name and plugin object + + #logger.info(self.Plugins) + #logger.info('AAAAAA') + self.IndexPlugins = dict(zip(plNameTmp, np.arange(0, plNameTmp.shape[0]))) # A dictionary with plugin name and plugin index @@ -105,7 +115,7 @@ def __init__(self, dataFiles = None, dictFiles = None): for i, [key,value] in enumerate(self.Plugins.items()): self.ui.tabWidget.insertTab(i, value, key) if i>0: - #self.ui.tabWidget.setTabVisible(i, False) + self.ui.tabWidget.setTabVisible(i, False) self.ui.tabWidget.setTabVisible(i, True) if dataFiles is not None: diff --git a/NiChartGUI/plugins/harmonizeview/__init__.py b/NiChartGUI/plugins/harmonizeview/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/NiChartGUI/plugins/harmonizeview/harmonizeview.py b/NiChartGUI/plugins/harmonizeview/harmonizeview.py new file mode 100644 index 0000000..7b8b7ae --- /dev/null +++ b/NiChartGUI/plugins/harmonizeview/harmonizeview.py @@ -0,0 +1,202 @@ +from PyQt5.QtGui import * +from PyQt5 import QtGui, QtCore, QtWidgets, uic +from PyQt5.QtWidgets import QMdiArea, QMdiSubWindow, QLineEdit, QComboBox, QMenu, QAction, QWidgetAction +import sys, os +import pandas as pd +import gzip +import pickle +import numpy as np +from NiChartGUI.core.dataio import DataIO +# import dtale +from NiChartGUI.core.baseplugin import BasePlugin +from NiChartGUI.core import iStagingLogger +from NiChartGUI.core.gui.SearchableQComboBox import SearchableQComboBox +from NiChartGUI.core.gui.CheckableQComboBox import CheckableQComboBox +from NiChartGUI.core.gui.NestedQMenu import NestedQMenu +from NiChartGUI.core.model.datamodel import PandasModel + +import inspect + +import sys + +from NiChartHarmonize import nh_apply_model as nh_test + +logger = iStagingLogger.get_logger(__name__) + +class HarmonizeView(QtWidgets.QWidget,BasePlugin): + + def __init__(self): + super(HarmonizeView,self).__init__() + + self.data_model_arr = None + self.active_index = -1 + + self.cmds = None + + self.modelname = None + ##self.modelname = '/home/guraylab/AIBIL/Github/TmpPackages/HarmonizeScores/mdl/mdl_SPARE_AD_MUSE_single.pkl.gz' + + ## Status bar of the main window + ## Initialized by the mainwindow during loading of plugin + self.statusbar = None + + + ## Status bar of the main window + ## Initialized by the mainwindow during loading of plugin + self.statusbar = None + + root = os.path.dirname(__file__) + self.readAdditionalInformation(root) + self.ui = uic.loadUi(os.path.join(root, 'harmonizeview.ui'),self) + + ## Main view panel + self.mdi = self.findChild(QMdiArea, 'mdiArea') + self.mdi.setBackground(QtGui.QColor(245,245,245,255)) + + ## Options panel is not shown if there is no dataset loaded + self.ui.wOptions.hide() + + self.ui.wOptions.setMaximumWidth(300) + + self.ui.wCalcHarmonize.hide() + + + + def SetupConnections(self): + + self.data_model_arr.active_dset_changed.connect(self.OnDataChanged) + + self.ui.selectModelBtn.clicked.connect(self.OnSelectModelBtnClicked) + self.ui.calcHarmonizeBtn.clicked.connect(self.OnCalcHarmonizeBtnClicked) + + + def CheckModel(self, filename): + #read input data + + # Load model + with gzip.open(filename, 'rb') as f: + self.mdl = pickle.load(f) + + # Get columns and check if they exist in dset + mdlCol = self.mdl['predictors'] + dfCol = self.data_model_arr.datasets[self.active_index].data.columns + + dfMdl = pd.DataFrame(columns=['Predictor'], data = mdlCol) + dfMdl['Status'] = dfMdl.Predictor.isin(dfCol) + dfMdl = dfMdl.replace({True:'FOUND', False:'MISSING'}).sort_values('Status', ascending = False) + + self.PopulateTable(dfMdl) + + ## Set data view to mdi widget + sub = QMdiSubWindow() + sub.setWidget(self.dataView) + sub.setWindowTitle('MODEL: ' + os.path.basename(filename)) + self.mdi.addSubWindow(sub) + sub.show() + self.mdi.tileSubWindows() + + if dfMdl[dfMdl.Status=='MISSING'].shape[0] > 0: + self.statusbar.showMessage('WARNING: Model does not match the data!') + + else: + self.ui.wCalcHarmonize.show() + self.statusbar.showMessage('Model is valid') + + + logger.critical(dfMdl.head()) + + def OnSelectModelBtnClicked(self): + + #if self.dataPathLast == '': + #directory = QtCore.QDir().homePath() + #else: + #directory = self.dataPathLast + directory = QtCore.QDir().homePath() + directory = '/home/guraylab/AIBIL/Github/TmpPackages/HarmonizeScores/mdl' + + filename = QtWidgets.QFileDialog.getOpenFileName(None, + caption = 'Open model file', + directory = directory, + filter = "Pickle/pickle.gz files (*.pkl *.gz)") + + if filename[0] == "": + logger.warning("No file was selected") + else: + self.modelname = filename[0] + self.ui.wCalcHarmonize.show() + + + def OnCalcHarmonizeBtnClicked(self): + + ## Read data and harmonize options + df = self.data_model_arr.datasets[self.active_index].data + outVarName = self.ui.edit_outVarName.text() + if outVarName == '': + outVarName = 'HARM' + if outVarName[0] == '_': + outVarName = outVarName[1:] + outCat = outVarName + + ## Apply Harmonization + res_harm = nh_test.nh_harmonize_to_ref(df, self.modelname) + + if len(res_harm) == 1: ## Model mismatch + logger.warning('AAAAAAAAAAAAAAAAAAA') + + else: + mdlOut, dfOut = res_harm + + ## Set updated dset + df = dfOut + self.data_model_arr.datasets[self.active_index].data = df + + ## Create dict with info about new columns + outDesc = 'Created by NiChartHarmonize Plugin' + outSource = 'NiChartHarmonize Plugin' + ##self.data_model_arr.AddNewVarsToDict([outVarName], outCat, outDesc, outSource) + + ## Call signal for change in data + ##self.data_model_arr.OnDataChanged() + + ## Load data to data view + self.dataView = QtWidgets.QTableView() + + ## Show only columns involved in application + + #dfOut = dfOut.round(3) + self.PopulateTable(dfOut) + + ## Set data view to mdi widget + sub = QMdiSubWindow() + sub.setWidget(self.dataView) + sub.setWindowTitle('Harmonized Values') + self.mdi.addSubWindow(sub) + sub.show() + self.mdi.tileSubWindows() + + + def PopulateTable(self, data): + + model = PandasModel(data) + self.dataView = QtWidgets.QTableView() + self.dataView.setModel(model) + + def OnDataChanged(self): + + if self.data_model_arr.active_index >= 0: + + ## Make options panel visible + self.ui.wOptions.show() + + ## Set fields for various options + self.active_index = self.data_model_arr.active_index + + ## Get data variables + dataset = self.data_model_arr.datasets[self.active_index] + + ## Set active dset name + self.ui.edit_activeDset.setText(self.data_model_arr.dataset_names[self.active_index]) + + ### Update selection, sorting and drop duplicates panels + #self.UpdatePanels(catNames, colNames) + diff --git a/NiChartGUI/plugins/harmonizeview/harmonizeview.ui b/NiChartGUI/plugins/harmonizeview/harmonizeview.ui new file mode 100644 index 0000000..b71a952 --- /dev/null +++ b/NiChartGUI/plugins/harmonizeview/harmonizeview.ui @@ -0,0 +1,285 @@ + + + Form + + + + 0 + 0 + 800 + 500 + + + + Form + + + + + + + + + + + + + + Qt::Horizontal + + + + + + 8 + 0 + + + + + + + + + + + + + + 1 + 0 + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 8 + + + + + + + + + Active Dataset + + + + + + + + + + Qt::Horizontal + + + + QSizePolicy::Fixed + + + + + 30 + 20 + + + + + + + + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 8 + + + + + + + + Model + + + + + + + + + + Qt::Horizontal + + + + QSizePolicy::Fixed + + + + + 30 + 20 + + + + + + + + Select Model + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 8 + + + + + + Output Variable Name: + + + + + + + + + Qt::Horizontal + + + + QSizePolicy::Fixed + + + + + 30 + 20 + + + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 8 + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Harmonize + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + + + diff --git a/NiChartGUI/plugins/harmonizeview/harmonizeview.yapsy-plugin b/NiChartGUI/plugins/harmonizeview/harmonizeview.yapsy-plugin new file mode 100644 index 0000000..3457d6f --- /dev/null +++ b/NiChartGUI/plugins/harmonizeview/harmonizeview.yapsy-plugin @@ -0,0 +1,15 @@ +[Core] +Name = Harmonize View +Module = harmonizeview + +[Documentation] +Author = Guray Erus +Version = 0.1 +Website = +Description = ---------- Description ---------- + Plugin for calculating the HARMONIZED values + ---------- Options ---------- + +[Tab] +#tab position starts from 0 +Position = 8 diff --git a/NiChartGUI/plugins/spareview/spareview.py b/NiChartGUI/plugins/spareview/spareview.py index 5b40610..66c2d38 100644 --- a/NiChartGUI/plugins/spareview/spareview.py +++ b/NiChartGUI/plugins/spareview/spareview.py @@ -18,8 +18,10 @@ import inspect import sys -sys.path.append('/cbica/home/erusg/3_DEV/SPARE-Scores/05_niCHART/packaging/spare_scores') -import spare_scores as spare +#sys.path.append('/cbica/home/erusg/3_DEV/SPARE-Scores/05_niCHART/packaging/spare_scores') +#import spare_scores as spare +from spare_scores import spare_train + logger = iStagingLogger.get_logger(__name__) diff --git a/NiChartGUI/plugins/spareview/spareview.yapsy-plugin b/NiChartGUI/plugins/spareview/spareview.yapsy-plugin index c7ae191..f33813b 100644 --- a/NiChartGUI/plugins/spareview/spareview.yapsy-plugin +++ b/NiChartGUI/plugins/spareview/spareview.yapsy-plugin @@ -12,4 +12,4 @@ Description = ---------- Description ---------- [Tab] #tab position starts from 0 -Position = 8 +Position = 9 diff --git a/pyproject.toml b/pyproject.toml index 78d2859..b7abf27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ requires = ['cycler==0.10.0', 'nibabel==3.2.1', 'numpy>=1.21', 'git+https://github.com/rpomponio/neuroHarmonize@refs/pull/26/head#egg=neuroHarmonize', -'pandas==1.3.4', +'pandas==2.0.1', 'Pillow>=9.0.0', 'pyparsing==2.4.7', 'PyQt5>=5.15.4', @@ -100,7 +100,7 @@ matplotlib = "^3.4.2" nibabel = "^3.2.1" numpy = "^1.21" neuroHarmonize = { git = "https://github.com/rpomponio/neuroHarmonize.git", branch = "refs/pull/26/head" } -pandas = "1.3.4" +pandas = "2.0.1" Pillow = "^9.0.0" pyparsing = "^2.4.7" PyQt5 = "^5.15.4" diff --git a/requirements.txt b/requirements.txt index 79b04d4..6a240ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ matplotlib>=3.4.2 nibabel==3.2.1 numpy>=1.21 git+https://github.com/rpomponio/neuroHarmonize@refs/pull/26/head -pandas==1.3.4 +pandas==2.0.1 Pillow>=9.0.0 poetry>=1.1.13 poetry_core>=1.0.7 diff --git a/testing/tmp_test1.sh b/testing/tmp_test1.sh new file mode 100644 index 0000000..eeba623 --- /dev/null +++ b/testing/tmp_test1.sh @@ -0,0 +1,10 @@ + +conda activate NiChartGUI +pip install . + + +ddir='/home/guraylab/AIBIL/Github/NiChartPackages/NiChartHarmonize/test_temp/Test3/outputs/EXP2_ALL_TrainTest' + +NiChartGUI --data_file ${ddir}/Set_Test.csv --data_file ${ddir}/Set_Train.csv + +NiChartGUI --data_file ${ddir}/Set_Test.csv