Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SPARE-* plugin #75

Merged
merged 1 commit into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion QtBrainChartGUI/core/model/datamodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ def IsValidSPARE(self):

def GetColumnHeaderNames(self):
"""Returns all header names for all columns in the dataset."""
k = self.data.keys()
if self.data is not None:
k = self.data.keys()
else:
k = []

return k


Expand Down
14 changes: 11 additions & 3 deletions QtBrainChartGUI/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#from BrainChart.dataio import DataIO
from QtBrainChartGUI.core.model.datamodel import DataModel

import pandas as pd

class MainWindow(QtWidgets.QMainWindow):
def __init__(self, dataFile=None, harmonizationModelFile=None, SPAREModelFile=None):
super(MainWindow,self).__init__()
Expand Down Expand Up @@ -45,9 +47,10 @@ def __init__(self, dataFile=None, harmonizationModelFile=None, SPAREModelFile=No
self.ui.tabWidget.addTab(po,plugin.name)

if dataFile is not None:
pass
# if datafile provided on cmd line, load it
#self.OnDataFileOpenClicked(dataFile)
self.OnDataFile(dataFile)


if harmonizationModelFile is not None:
pass
#if harmonization model file provided on cmd line, load it
Expand All @@ -64,7 +67,7 @@ def SetupUi(self):
print("in setup")
root = os.path.dirname(__file__)
self.ui = uic.loadUi(os.path.join(root, 'mainwindow.ui'), self)
self.ui.setWindowTitle('Qt BrainChart')
self.ui.setWindowTitle('Neuro-imaging brain aging chart')

def OnAboutClicked(self):
#quit application
Expand All @@ -76,6 +79,11 @@ def OnCloseClicked(self):
QtWidgets.QApplication.quit()


def OnDataFile(self, dataFile):
self.datamodel.SetDataFilePath(dataFile)
self.datamodel.SetData(pd.read_pickle(dataFile))


def ResetUI(self):
#reset all UI
pass
7 changes: 5 additions & 2 deletions QtBrainChartGUI/mainwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
</rect>
</property>
<property name="windowTitle">
<string>Qt BrainChart</string>
<string>NiBAX</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
Expand All @@ -37,7 +40,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuAbout">
Expand Down
166 changes: 166 additions & 0 deletions QtBrainChartGUI/plugins/computeSPAREs/computeSPAREs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
from PyQt5.QtGui import *
from yapsy.IPlugin import IPlugin
from PyQt5 import QtGui, QtCore, QtWidgets, uic
import joblib
import sys, os

import seaborn as sns
import numpy as np
import pandas as pd

# Plotting with matplotlib
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib as mpl
mpl.use('QT5Agg')


class PlotCanvas(FigureCanvas):
""" A generic Plotting class that derives from FigureCanvasQTAgg
and plots data as per different options"""

def __init__(self, parent=None, width=5, height=4, dpi=100):
"""The constructor."""

# a figure instance to plot on
fig = mpl.figure.Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)

#super clas
FigureCanvas.__init__(self, fig)
self.setParent(parent)

FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)

class computeSPAREs(QtWidgets.QWidget,IPlugin):

#constructor
def __init__(self):
super(computeSPAREs,self).__init__()
self.model = {'BrainAge': None, 'AD': None}
root = os.path.dirname(__file__)
self.ui = uic.loadUi(os.path.join(root, 'computeSPAREs.ui'),self)
self.plotCanvas = PlotCanvas(self.ui.page_2)
self.ui.verticalLayout.addWidget(self.plotCanvas)
self.SPAREs = None
#TODO: hook up plot functionality

self.ui.stackedWidget.setCurrentIndex(0)


def getUI(self):
return self.ui


def SetupConnections(self):
#pass
self.ui.load_SPARE_model_Btn.clicked.connect(lambda: self.OnLoadSPAREModel())
self.ui.load_other_model_Btn.clicked.connect(lambda: self.OnLoadSPAREModel())
self.ui.add_to_dataframe_Btn.clicked.connect(lambda: self.OnAddToDataFrame())
self.ui.compute_SPARE_scores_Btn.clicked.connect(lambda: self.OnComputeSPAREs())
self.ui.show_SPARE_scores_from_data_Btn.clicked.connect(lambda: self.OnShowSPAREs())
self.datamodel.data_changed.connect(lambda: self.OnDataChanged())
# Set `Show SPARE-* from data` button to visible when SPARE-* columns
# are present in data frame
if ('SPARE_BA' in self.datamodel.GetColumnHeaderNames() and
'SPARE_AD' in self.datamodel.GetColumnHeaderNames()):
self.ui.show_SPARE_scores_from_data_Btn.setEnabled(True)
else:
self.ui.show_SPARE_scores_from_data_Btn.setEnabled(False)



def OnLoadSPAREModel(self):
fileName, _ = QtWidgets.QFileDialog.getOpenFileName(None,
'Open SPARE-* model file',
QtCore.QDir().homePath(),
"Pickle files (*.pkl.gz *.pkl)")
if fileName != "":
self.model['BrainAge'], self.model['AD'] = joblib.load(fileName)
self.ui.compute_SPARE_scores_Btn.setEnabled(True)

self.ui.stackedWidget.setCurrentIndex(0)


def OnComputeSPAREs(self):
self.SPAREs = pd.DataFrame.from_dict({
'SPARE_BA': predictBrainAge(self.datamodel.data, self.model['BrainAge']),
'SPARE_AD': predictAD(self.datamodel.data, self.model['AD'])})
self.plotSPAREs()
self.ui.stackedWidget.setCurrentIndex(1)


def plotSPAREs(self):
sns.scatterplot(x='SPARE_AD', y='SPARE_BA', data=self.SPAREs,
ax=self.plotCanvas.axes)


def OnAddToDataFrame(self):
pass


def OnShowSPAREs(self):
self.SPAREs = pd.DataFrame.from_dict({
'SPARE_BA': self.datamodel.data['SPARE_BA'].values,
'SPARE_AD': self.datamodel.data['SPARE_AD'].values})
self.plotSPAREs()
self.ui.stackedWidget.setCurrentIndex(1)


def OnDataChanged(self):
# Set `Show SPARE-* from data` button to visible when SPARE-* columns
# are present in data frame
if ('SPARE_BA' in self.datamodel.GetColumnHeaderNames() and
'SPARE_AD' in self.datamodel.GetColumnHeaderNames()):
self.ui.show_SPARE_scores_from_data_Btn.setEnabled(True)
else:
self.ui.show_SPARE_scores_from_data_Btn.setEnabled(False)



def predictBrainAge(data,model):

idx = ~data[model['predictors'][0]].isnull()

y_hat_test = np.zeros((np.sum(idx),))
n_ensembles = np.zeros((np.sum(idx),))

for i,_ in enumerate(model['scaler']):
# Predict validation (fold) and test
print('Fold %d' % (i))
test = np.logical_not(data[idx]['participant_id'].isin(np.concatenate(model['train']))) | data[idx]['participant_id'].isin(model['validation'][i])
X = data[idx].loc[test, model['predictors']].values
X = model['scaler'][i].transform(X)
y_hat_test[test] += (model['svm'][i].predict(X) - model['bias_ints'][i]) / model['bias_slopes'][i]
n_ensembles[test] += 1.

y_hat_test /= n_ensembles
y_hat = np.full((data.shape[0],),np.nan)
y_hat[idx] = y_hat_test

return y_hat


def predictAD(data,model):

idx = ~data[model['predictors'][0]].isnull()

y_hat_test = np.zeros((np.sum(idx),))
n_ensembles = np.zeros((np.sum(idx),))

for i,_ in enumerate(model['scaler']):
# Predict validation (fold) and test
print('Fold %d' % (i))
test = np.logical_not(data[idx]['participant_id'].isin(np.concatenate(model['train']))) | data[idx]['participant_id'].isin(model['validation'][i])
X = data[idx].loc[test, model['predictors']].values
X = model['scaler'][i].transform(X)
y_hat_test[test] += model['svm'][i].decision_function(X)
n_ensembles[test] += 1.

y_hat_test /= n_ensembles
y_hat = np.full((data.shape[0],),np.nan)
y_hat[idx] = y_hat_test

return y_hat
91 changes: 91 additions & 0 deletions QtBrainChartGUI/plugins/computeSPAREs/computeSPAREs.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="page">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="show_SPARE_scores_from_data_Btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Show SPARE-* from data</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="load_SPARE_model_Btn">
<property name="text">
<string>Load SPARE Model</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="compute_SPARE_scores_Btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Compute SPARE-*</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>SPARE-* model details</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="load_other_model_Btn">
<property name="text">
<string>Load other model</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="add_to_dataframe_Btn">
<property name="text">
<string>Add to DataFrame</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Core]
Name = SPARE-*
Module = computeSPAREs

[Documentation]
Author = Ahmed Abdulkadir
Version = 0.1
Website =
Description = Compute SPARE-* scores from existing model.