Skip to content

Commit 5a231df

Browse files
committed
OWVolcanoPlot: general improvements, use of GeneScoring component
1 parent 6a7d577 commit 5a231df

File tree

7 files changed

+540
-73
lines changed

7 files changed

+540
-73
lines changed

orangecontrib/bioinformatics/tests/widgets/components/__init__.py

Whitespace-only changes.
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import unittest
2+
3+
import numpy as np
4+
5+
from AnyQt.QtCore import QItemSelection, QItemSelectionModel
6+
from AnyQt.QtTest import QSignalSpy
7+
8+
from Orange.data import Table
9+
from Orange.preprocess import Remove
10+
from Orange.widgets.widget import OWWidget
11+
from Orange.widgets.settings import SettingProvider
12+
from Orange.widgets.tests.base import WidgetTest
13+
from Orange.widgets.tests.utils import simulate
14+
15+
from orangecontrib.bioinformatics.utils.statistics import score_hypergeometric_test
16+
from orangecontrib.bioinformatics.widgets.components import GeneScoringComponent
17+
from orangecontrib.bioinformatics.widgets.utils.data import TableAnnotation
18+
19+
20+
class MockWidget(OWWidget):
21+
name = "Mock"
22+
scoring_component = SettingProvider(GeneScoringComponent)
23+
24+
def __init__(self):
25+
self.scoring_component = GeneScoringComponent(self, self.mainArea)
26+
27+
28+
def iris_test_case(data: Table):
29+
class TestGeneScoringComponent(WidgetTest):
30+
def setUp(self):
31+
self.widget = MockWidget()
32+
self.component = self.widget.scoring_component
33+
34+
def test_expression_threshold_spinbox(self):
35+
# find index of item in combobox for hypergeometric test
36+
method_index, *_ = [
37+
index
38+
for index, (name, method) in enumerate(self.component.score_methods)
39+
if method == score_hypergeometric_test
40+
]
41+
42+
# check if spinbox appears after hypergeometric test is selected
43+
self.assertTrue(self.component.expression_threshold_box.isHidden())
44+
simulate.combobox_activate_index(self.component.score_method_combo, method_index)
45+
self.assertFalse(self.component.expression_threshold_box.isHidden())
46+
47+
def test_scoring_methods_combobox(self):
48+
combo_box_values = [
49+
self.component.score_method_combo.itemText(i) for i in range(self.component.score_method_combo.count())
50+
]
51+
self.assertTrue(len(combo_box_values) > 0)
52+
self.assertEqual([name for name, _ in self.component.score_methods], combo_box_values)
53+
54+
signals_cb_emits = QSignalSpy(self.component.score_method_changed)
55+
simulate.combobox_run_through_all(self.component.score_method_combo)
56+
57+
self.assertEqual(self.component.score_method_combo.currentIndex(), self.component.current_method_index)
58+
self.assertEqual(self.component.current_method_index, len(combo_box_values) - 1)
59+
60+
# number of signals combobox emits should be equal to the length of available scoring methods
61+
self.assertEqual(len(combo_box_values), len(signals_cb_emits))
62+
63+
def test_selected_group_values(self):
64+
self.assertIsNone(self.component.data)
65+
self.component.initialize(data)
66+
self.assertIsNotNone(self.component.data)
67+
68+
# we expect only one value 'iris'
69+
combo_box_value, *_ = [
70+
self.component.group_combo.itemText(i) for i in range(self.component.group_combo.count())
71+
]
72+
self.assertEqual(combo_box_value, 'iris')
73+
74+
group_values = [
75+
self.component.list_widget.item(i).text() for i in range(self.component.list_widget.count())
76+
]
77+
self.assertEqual(group_values, ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'])
78+
79+
def test_selection(self):
80+
self.component.initialize(data)
81+
list_widget = self.component.list_widget
82+
signals_cb_emits = QSignalSpy(self.component.selection_changed)
83+
84+
# get modelIndex from list widget
85+
iris_setosa_index = list_widget.indexFromItem(list_widget.item(0))
86+
iris_versicolor_index = list_widget.indexFromItem(list_widget.item(1))
87+
88+
# set selection
89+
selection = QItemSelection()
90+
selection.select(iris_setosa_index, iris_setosa_index)
91+
selection.select(iris_versicolor_index, iris_versicolor_index)
92+
list_widget.selectionModel().select(selection, QItemSelectionModel.ClearAndSelect)
93+
94+
# test if correct number of signals is emited
95+
self.assertEqual(1, len(signals_cb_emits))
96+
# test if selection is OK
97+
self.assertEqual(2, len(list_widget.selectedItems()))
98+
99+
selection_mask = self.component.get_selection_mask()
100+
_selection_mask = ~selection_mask
101+
102+
self.assertIsInstance(selection_mask, np.ndarray)
103+
self.assertTrue(selection_mask.dtype == np.bool)
104+
105+
if 'iris' in data.domain:
106+
# test selection mask
107+
self.assertEqual(data.X[selection_mask, :].shape, (100, 4))
108+
109+
remover = Remove(class_flags=Remove.RemoveUnusedValues)
110+
x1, x2 = remover(data[selection_mask, :]), remover(data[_selection_mask, :])
111+
112+
selected_row_values = x1.domain['iris'].values
113+
unselected_row_values = x2.domain['iris'].values
114+
115+
self.assertTrue(len(selected_row_values) == 2)
116+
self.assertIn('Iris-setosa', selected_row_values)
117+
self.assertIn('Iris-versicolor', selected_row_values)
118+
self.assertNotIn('Iris-virginica', selected_row_values)
119+
120+
self.assertTrue(len(unselected_row_values) == 1)
121+
self.assertIn('Iris-virginica', unselected_row_values)
122+
else:
123+
# test selection mask
124+
x = data.X
125+
self.assertEqual(x[:, selection_mask].shape, (4, 100))
126+
self.assertEqual(x[:, _selection_mask].shape, (4, 50))
127+
128+
selected_col_values = {
129+
col.attributes.get('iris')
130+
for col, selected in zip(data.domain.variables, selection_mask)
131+
if selected
132+
}
133+
unselected_col_values = {
134+
col.attributes.get('iris')
135+
for col, selected in zip(data.domain.variables, _selection_mask)
136+
if selected
137+
}
138+
139+
self.assertTrue(len(selected_col_values) == 2)
140+
self.assertIn('Iris-setosa', selected_col_values)
141+
self.assertIn('Iris-versicolor', selected_col_values)
142+
self.assertNotIn('Iris-virginica', selected_col_values)
143+
144+
self.assertTrue(len(unselected_col_values) == 1)
145+
self.assertIn('Iris-virginica', unselected_col_values)
146+
147+
return TestGeneScoringComponent
148+
149+
150+
iris = Table('iris')
151+
iris.attributes[TableAnnotation.gene_as_attr_name] = False
152+
153+
iris_transposed = Table.transpose(Table('iris'))
154+
iris_transposed.attributes[TableAnnotation.gene_as_attr_name] = True
155+
156+
157+
class TestRowGroup(iris_test_case(iris)):
158+
pass
159+
160+
161+
class TestColumnGroup(iris_test_case(iris_transposed)):
162+
pass
163+
164+
165+
if __name__ == "__main__":
166+
unittest.main()

orangecontrib/bioinformatics/utils/statistics.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
ALTERNATIVES = [ALT_GREATER, ALT_TWO, ALT_LESS]
1313

1414

15-
def score_t_test(a, b, axis=0, alternative=ALT_TWO):
16-
# type: (np.array, np.array, int, str) -> Tuple[Union[float, np.array], Union[float, np.array]]
15+
def score_t_test(
16+
a: np.array, b: np.array, axis: int = 0, **kwargs
17+
) -> Tuple[Union[float, np.array], Union[float, np.array]]:
1718
""" Run t-test. Enable setting different alternative hypothesis.
1819
Probabilities are exact due to symmetry of the test.
1920
@@ -24,7 +25,7 @@ def score_t_test(a, b, axis=0, alternative=ALT_TWO):
2425
scipy.stats.ttest_ind
2526
2627
"""
27-
# alt = kwargs.get("alternative", ALT_TWO)
28+
alternative = kwargs.get("alternative", ALT_TWO)
2829
assert alternative in ALTERNATIVES
2930
scores, pvalues = scipy.stats.ttest_ind(a, b, axis=axis)
3031

@@ -41,7 +42,7 @@ def score_t_test(a, b, axis=0, alternative=ALT_TWO):
4142
return scores, 1.0 - pvalues
4243

4344

44-
def score_mann_whitney(a, b, **kwargs):
45+
def score_mann_whitney(a: np.array, b: np.array, **kwargs) -> Tuple[np.array, np.array]:
4546
axis = kwargs.get('axis', 0)
4647
a, b = np.asarray(a, dtype=float), np.asarray(b, dtype=float)
4748

@@ -71,12 +72,15 @@ def score_mann_whitney(a, b, **kwargs):
7172
return np.array(statistics), np.array(p_values)
7273

7374

74-
def score_hypergeometric_test(a, b, threshold=1, **kwargs):
75+
def score_hypergeometric_test(a: np.array, b: np.array, threshold: float = 1.0, **kwargs) -> Tuple[np.array, np.array]:
7576
"""
7677
Run a hypergeometric test. The probability in a two-sided test is approximated
7778
with the symmetric distribution with more extreme of the tails.
7879
"""
79-
# type: (np.ndarray, np.ndarray, float) -> np.ndarray
80+
axis = kwargs.get('axis', 0)
81+
82+
if axis == 1:
83+
a, b = a.T, b.T
8084

8185
# Binary expression matrices
8286
_a = (a >= threshold).astype(int)

0 commit comments

Comments
 (0)