Skip to content

Commit 8646a92

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

File tree

6 files changed

+483
-75
lines changed

6 files changed

+483
-75
lines changed

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

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