Skip to content

Commit

Permalink
feat: rudimentary LMM analysis (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmueller committed Feb 12, 2021
1 parent eba19bf commit 79c659b
Show file tree
Hide file tree
Showing 13 changed files with 1,017 additions and 10 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
2.4.16
2.5.0
- feat: implement linear-mixed effects models with R/lme4 (#55)
- ci: overhauled build process
2.4.15
- ci: fix rtd build
Expand Down
1 change: 1 addition & 0 deletions shapeout2/gui/compute/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# flake8: noqa: F401
from .comp_lme4 import ComputeSignificance
from .comp_stats import ComputeStatistics
86 changes: 86 additions & 0 deletions shapeout2/gui/compute/comp_lme4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import pkg_resources
import webbrowser

from dclab import lme4
from PyQt5 import uic, QtCore, QtGui, QtWidgets

from .comp_lme4_dataset import LME4Dataset
from .comp_lme4_results import Rlme4ResultsDialog


class ComputeSignificance(QtWidgets.QDialog):
def __init__(self, parent, pipeline, *args, **kwargs):
super(ComputeSignificance, self).__init__(parent, *args, **kwargs)
path_ui = pkg_resources.resource_filename(
"shapeout2.gui.compute", "comp_lme4.ui")
uic.loadUi(path_ui, self)
# set pipeline
self.pipeline = pipeline

# populate feature combo box
feats, labs = pipeline.get_features(scalar=True, label_sort=True,
union=False, ret_labels=True)
for feat, lab in zip(feats, labs):
self.comboBox_feat.addItem(lab, feat)

# populate datasets
self.datasets = []
for slot in self.pipeline.slots:
dw = LME4Dataset(self, slot=slot)
self.dataset_layout.addWidget(dw)
self.datasets.append(dw)
spacer = QtWidgets.QSpacerItem(20, 0,
QtWidgets.QSizePolicy.Minimum,
QtWidgets.QSizePolicy.Expanding)
self.dataset_layout.addItem(spacer)
self.update()

# button signals
btn_close = self.buttonBox.button(QtGui.QDialogButtonBox.Close)
btn_close.clicked.connect(self.on_close)
btn_close.setToolTip("Close this dialog")
closeicon = QtGui.QIcon.fromTheme("dialog-close")
btn_close.setIcon(closeicon)
btn_openlme4 = self.buttonBox.button(QtGui.QDialogButtonBox.Apply)
btn_openlme4.clicked.connect(self.on_lme4)
btn_openlme4.setToolTip("Perform lme4 analysis")
btn_openlme4.setText("Run R-lme4")
picon = QtGui.QIcon.fromTheme("rlang")
btn_openlme4.setIcon(picon)
btn_help = self.buttonBox.button(QtGui.QDialogButtonBox.Help)
btn_help.clicked.connect(self.on_help)
btn_help.setToolTip("View R-lme4 Quick Guide online")
helpicon = QtGui.QIcon.fromTheme("documentinfo")
btn_help.setIcon(helpicon)

@property
def feature(self):
return self.comboBox_feat.currentData()

@property
def model(self):
if self.radioButton_lmer.isChecked():
return "lmer"
else:
return "glmer+loglink"

@QtCore.pyqtSlot()
def on_lme4(self):
"""Run lme4 analysis"""
rlme4 = lme4.Rlme4(model=self.model, feature=self.feature)
for wds in self.datasets:
wds.add_to_rlme4(self.pipeline, rlme4)
result = rlme4.fit()
dlg = Rlme4ResultsDialog(self, result)
dlg.exec_()

@QtCore.pyqtSlot()
def on_close(self):
"""Close window"""
self.close()

@QtCore.pyqtSlot()
def on_help(self):
"""Show Shape-Out 2 docs"""
webbrowser.open(
"https://dclab.readthedocs.io/en/stable/sec_av_lme4.html")
194 changes: 194 additions & 0 deletions shapeout2/gui/compute/comp_lme4.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>593</width>
<height>544</height>
</rect>
</property>
<property name="windowTitle">
<string>Compute statistical significance with R-lme4</string>
</property>
<property name="windowIcon">
<iconset theme="statistical_significance">
<normaloff>../../../../../.designer/backup</normaloff>../../../../../.designer/backup</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Compute the statistical significance using linear mixed-effects models</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Model:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_lmer">
<property name="toolTip">
<string>linear mixed-effects model</string>
</property>
<property name="text">
<string>lmer</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_glmer">
<property name="toolTip">
<string>generalized linear mixed-effects model with a log-link function</string>
</property>
<property name="text">
<string>glmer+loglink</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Feature:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_feat">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Datasets:</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>573</width>
<height>383</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QVBoxLayout" name="dataset_layout"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
50 changes: 50 additions & 0 deletions shapeout2/gui/compute/comp_lme4_dataset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pkg_resources

from PyQt5 import uic, QtGui, QtWidgets

from ... import meta_tool


class LME4Dataset(QtWidgets.QDialog):
def __init__(self, parent, slot, *args, **kwargs):
super(LME4Dataset, self).__init__(parent, *args, **kwargs)
path_ui = pkg_resources.resource_filename(
"shapeout2.gui.compute", "comp_lme4_dataset.ui")
uic.loadUi(path_ui, self)

self.identifier = slot.identifier

# set dataset label
self.checkBox_dataset.setText(slot.name)

# set region icon
region = meta_tool.get_info(slot.path,
section="setup",
key="chip region")
icon = QtGui.QIcon.fromTheme("region_{}".format(region))
pixmap = icon.pixmap(16)
self.label_region.setPixmap(pixmap)
self.label_region.setToolTip(region)

def add_to_rlme4(self, pipeline, rlme4):
"""Add the dataset to an Rlme4 analysis
Parameters
----------
pipeline: shapeout2.pipeline.core.Pipeline
The pipeline from which to extract the filtered dataset
using `self.identifier`.
rlme4: dclab.lme4.wrapr.Rlme4
The analysis to which to append this dataset.
Notes
-----
If the check box is not checked, then the dataset is ignored.
"""
if self.checkBox_dataset.isChecked():
ds_index = pipeline.slot_ids.index(self.identifier)
ds = pipeline.get_dataset(ds_index)
group_id = self.comboBox_group.currentIndex()
group = "control" if group_id == 0 else "treatment"
repetition = self.spinBox_repeat.value()
rlme4.add_dataset(ds=ds, group=group, repetition=repetition)
Loading

0 comments on commit 79c659b

Please sign in to comment.