Skip to content

Commit

Permalink
owmarkergenes: Add 'Marker Genes' widget
Browse files Browse the repository at this point in the history
  • Loading branch information
ales-erjavec committed Feb 12, 2018
1 parent abdd64e commit 502962f
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 1 deletion.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
include orangecontrib/single_cell/launcher/icons/*
include orangecontrib/single_cell/tutorials/*.ows
include orangecontrib/single_cell/widgets/icons/*
include orangecontrib/single_cell/widgets/data/*
include orangecontrib/single_cell/datasets/*

include requirements.txt
Expand Down
16 changes: 16 additions & 0 deletions orangecontrib/single_cell/widgets/data/markers.tab
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Organism Name ID Cell Type
string string string string
meta meta meta
Human S100A9 ENSG00000163220 Monocyte
Human PPBP ENSG00000163736 Megakaryocyte
Human CD14 ENSG00000170458 Monocyte
Human CD3D ENSG00000167286 T Cell
Human CD4 ENSG00000010610 T Cell
Human CD19 ENSG00000177455 B Cell
Human CD79A ENSG00000105369 B Cell
Human NKG7 ENSG00000105374 NK Cell
Human CD146 Endothelial Cell
Mus musculus Cd3d ENSMUSG00000032094 T Cell
Mus musculus CD4 T Cell
Human CD34 ENSG00000174059 Stem Cell/Precursor
Mus musculus CD34 ENSMUSG00000016494 Stem Cell/Precursor
189 changes: 189 additions & 0 deletions orangecontrib/single_cell/widgets/owmarkergenes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import sys
import os

from AnyQt.QtCore import Qt, QSize, QSortFilterProxyModel, QModelIndex
from AnyQt.QtWidgets import QTreeView, QLineEdit

import Orange.data

from Orange.widgets import widget, gui, settings
from Orange.widgets.utils.itemmodels import TableModel


def resource_path(path):
return os.path.join(os.path.dirname(__file__), path)


class FilterProxyModel(QSortFilterProxyModel):
def filterAcceptsRow(self, source_row, source_parent):
# type: (int, QModelIndex) -> bool
model = self.sourceModel()
if isinstance(model, TableModel):
table = model.source
domain = table.domain
index = model.mapToSourceRows(source_row)
row = table[index]
regexp = self.filterRegExp()
return any(regexp.indexIn(text) != -1 for text in
(str(row[var]) if var.is_string else var.str_val(row[var])
for var in domain.variables + domain.metas
if var.is_string or var.is_discrete))
else:
return True


class OWMarkerGenes(widget.OWWidget):
name = "Marker Genes"

class Outputs:
genes = widget.Output("Genes", Orange.data.Table)

want_main_area = False

selected_group = settings.Setting("") # type: str
header_state = settings.Setting(b'') # type: bytes

def __init__(self):
super().__init__()
self.source = None
self.group_index = -1
self.filter_text = ""
self.group_cb = gui.comboBox(self.controlArea, self, "group_index")
self.group_cb.activated[int].connect(self.set_group_index)

filter = gui.lineEdit(
self.controlArea, self, "filter_text"
) # type: QLineEdit
filter.setPlaceholderText("Filter...")
filter.textEdited.connect(self.set_filter_str)
self.view = view = QTreeView(
rootIsDecorated=False,
uniformRowHeights=True,
selectionMode=QTreeView.ExtendedSelection,
sortingEnabled=True,
)

self.proxy_model = FilterProxyModel(
self, filterCaseSensitivity=Qt.CaseInsensitive,
)
view.setModel(self.proxy_model)
view.selectionModel().selectionChanged.connect(
self._on_selection_changed
)
self.controlArea.layout().addWidget(view)

# NEEDS updating/
self.set_source(Orange.data.Table(resource_path("data/markers.tab")))
if self.header_state:
view.header().restoreState(self.header_state)
self.commit()

def set_source(self, data):
# type: (Orange.data.Table) -> None
"""
Set the source data from which to fetch the output
The output is a subset filtered on the first meta column (group)
"""
self.source = data
domain = data.domain
if domain.metas:
group = domain.metas[0]
groupcol, _ = data.get_column_view(group)
if group.is_string:
group_values = list(map(str, unique(groupcol)))
elif group.is_discrete:
group_values = group.values
else:
raise TypeError("Invalid column type")
try:
idx = group_values.index(self.selected_group)
except ValueError:
idx = -1
self.group_cb.clear()
self.group_cb.addItems(group_values)
if idx != -1:
self.group_index = idx
self.selected_group = group_values[idx]
elif group_values:
self.group_index = min(self.group_index, len(group_values) - 1)

self._setup()

def set_group_index(self, group_index):
self.group_index = group_index
self.selected_group = self.group_cb.itemText(group_index)
self._setup()

def set_filter_str(self, string):
if string != self.filter_text:
self.filter_text = string
proxy = self.view.model()
assert isinstance(proxy, QSortFilterProxyModel)
proxy.setFilterFixedString(string)

def _setup(self):
data = self.source
group = data.domain.metas[0]
gvec = data.get_column_view(group)[0]
if group.is_string:
mask = gvec == self.group_cb.itemData(self.group_index,
Qt.DisplayRole)
else:
mask = gvec == self.group_index

data = data[mask]
rest = data[:, data.domain.metas[1:]]
model = TableModel(rest, parent=self)
if self.proxy_model.sourceModel():
self.proxy_model.sourceModel().deleteLater()
self.proxy_model.setSourceModel(model)
self.commit()

def _on_selection_changed(self):
self.commit()

def commit(self):
table = self.view.model().sourceModel()
assert isinstance(table, TableModel)
rows = [mi.row() for mi in self.view.selectionModel().selectedRows(0)]
if rows:
rows = table.mapToSourceRows(rows)
output = table.source[rows]
else:
output = table.source
self.Outputs.genes.send(output)

def closeEvent(self, event):
self.header_state = bytes(self.view.header().saveState())
super().closeEvent(event)

def sizeHint(self):
return super().sizeHint().expandedTo(QSize(600, 500))


def unique(iterable):
seen = set()

def observed(el):
observed = el in seen
seen.add(el)
return observed

return (el for el in iterable if not observed(el))


def main(argv=None):
from AnyQt.QtWidgets import QApplication
app = QApplication(argv or sys.argv)
w = OWMarkerGenes()
w.show()
w.activateWindow()
rv = app.exec_()
w.saveSettings()
w.onDeleteWidget()
return rv


if __name__ == "__main__":
sys.exit(main())
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
PACKAGE_DATA = {
'orangecontrib.single_cell': ['tutorials/*.ows'],
'orangecontrib.single_cell.launcher': ['icons/*'],
'orangecontrib.single_cell.widgets': ['icons/*'],
'orangecontrib.single_cell.widgets': ['icons/*', 'data/*']
}

DATA_FILES = [
Expand Down

0 comments on commit 502962f

Please sign in to comment.