Skip to content

Commit ea6e832

Browse files
committed
owheatmap: option to center palette
1 parent 747beb7 commit ea6e832

File tree

2 files changed

+56
-13
lines changed

2 files changed

+56
-13
lines changed

Orange/widgets/visualize/owheatmap.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,12 @@ def color_palette_table(colors,
213213
return np.c_[r, g, b]
214214

215215

216-
def levels_with_thresholds(low, high, threshold_low, threshold_high):
216+
def levels_with_thresholds(low, high, threshold_low, threshold_high, center_palette):
217217
lt = low + (high - low) * threshold_low
218218
ht = low + (high - low) * threshold_high
219+
if center_palette:
220+
ht = max(abs(lt), abs(ht))
221+
lt = -max(abs(lt), abs(ht))
219222
return lt, ht
220223

221224

@@ -398,6 +401,8 @@ class Outputs:
398401
selected_data = Output("Selected Data", Table, default=True)
399402
annotated_data = Output(ANNOTATED_DATA_SIGNAL_NAME, Table)
400403

404+
settings_version = 2
405+
401406
settingsHandler = settings.DomainContextHandler()
402407

403408
NoPosition, PositionTop, PositionBottom = 0, 1, 2
@@ -408,6 +413,7 @@ class Outputs:
408413
gamma = settings.Setting(0)
409414
threshold_low = settings.Setting(0.0)
410415
threshold_high = settings.Setting(1.0)
416+
center_palette = settings.Setting(False)
411417

412418
merge_kmeans = settings.Setting(False)
413419
merge_kmeans_k = settings.Setting(50)
@@ -526,6 +532,9 @@ def __init__(self):
526532

527533
colorbox.layout().addLayout(form)
528534

535+
gui.checkBox(colorbox, self, 'center_palette', 'Center colors at 0',
536+
callback=self.update_color_schema)
537+
529538
mergebox = gui.vBox(self.controlArea, "Merge",)
530539
gui.checkBox(mergebox, self, "merge_kmeans", "Merge by k-means",
531540
callback=self.update_sorting_examples)
@@ -1043,7 +1052,7 @@ def setup_scene(self, parts, data):
10431052

10441053
hw.set_levels(parts.levels)
10451054
hw.set_thresholds(self.threshold_low, self.threshold_high)
1046-
hw.set_color_table(palette)
1055+
hw.set_color_table(palette, self.center_palette)
10471056
hw.set_show_averages(self.averages)
10481057
hw.set_heatmap_data(X_part)
10491058

@@ -1118,7 +1127,7 @@ def setup_scene(self, parts, data):
11181127
parts.levels[0], parts.levels[1], self.threshold_low, self.threshold_high,
11191128
parent=widget)
11201129

1121-
legend.set_color_table(palette)
1130+
legend.set_color_table(palette, self.center_palette)
11221131
legend.setMinimumSize(QSizeF(100, 20))
11231132
legend.setVisible(self.legend)
11241133

@@ -1379,11 +1388,11 @@ def update_color_schema(self):
13791388
palette = self.color_palette()
13801389
for heatmap in self.heatmap_widgets():
13811390
heatmap.set_thresholds(self.threshold_low, self.threshold_high)
1382-
heatmap.set_color_table(palette)
1391+
heatmap.set_color_table(palette, self.center_palette)
13831392

13841393
for legend in self.legend_widgets():
13851394
legend.set_thresholds(self.threshold_low, self.threshold_high)
1386-
legend.set_color_table(palette)
1395+
legend.set_color_table(palette, self.center_palette)
13871396

13881397
def update_sorting_examples(self):
13891398
self.update_heatmaps()
@@ -1660,6 +1669,7 @@ def __init__(self, parent=None, data=None, **kwargs):
16601669

16611670
self.__levels = None
16621671
self.__threshold_low, self.__threshold_high = 0., 1.
1672+
self.__center_palette = False
16631673
self.__colortable = None
16641674
self.__data = data
16651675

@@ -1736,8 +1746,9 @@ def set_show_averages(self, show):
17361746
self.layout().invalidate()
17371747
self.update()
17381748

1739-
def set_color_table(self, table):
1749+
def set_color_table(self, table, center):
17401750
self.__colortable = table
1751+
self.__center_palette = center
17411752
self._update_pixmap()
17421753
self.update()
17431754

@@ -1758,7 +1769,8 @@ def _update_pixmap(self):
17581769
lut = None
17591770

17601771
ll, lh = self.__levels
1761-
ll, lh = levels_with_thresholds(ll, lh, self.__threshold_low, self.__threshold_high)
1772+
ll, lh = levels_with_thresholds(ll, lh, self.__threshold_low, self.__threshold_high,
1773+
self.__center_palette)
17621774

17631775
argb, _ = pg.makeARGB(
17641776
self.__data, lut=lut, levels=(ll, lh))
@@ -2117,6 +2129,7 @@ def __init__(self, low, high, threshold_low, threshold_high, parent=None):
21172129
self.high = high
21182130
self.threshold_low = threshold_low
21192131
self.threshold_high = threshold_high
2132+
self.center_palette = False
21202133
self.color_table = None
21212134

21222135
layout = QGraphicsLinearLayout(Qt.Vertical)
@@ -2143,8 +2156,9 @@ def __init__(self, low, high, threshold_low, threshold_high, parent=None):
21432156
layout.addItem(self.__pixitem)
21442157
self.__update()
21452158

2146-
def set_color_table(self, color_table):
2159+
def set_color_table(self, color_table, center):
21472160
self.color_table = color_table
2161+
self.center_palette = center
21482162
self.__update()
21492163

21502164
def set_thresholds(self, threshold_low, threshold_high):
@@ -2156,7 +2170,8 @@ def __update(self):
21562170
data = np.linspace(self.low, self.high, num=1000)
21572171
data = data.reshape((1, -1))
21582172
ll, lh = levels_with_thresholds(self.low, self.high,
2159-
self.threshold_low, self.threshold_high)
2173+
self.threshold_low, self.threshold_high,
2174+
self.center_palette)
21602175
argb, _ = pg.makeARGB(data, lut=self.color_table,
21612176
levels=(ll, lh))
21622177
qimg = pg.makeQImage(argb, transpose=False)

Orange/widgets/visualize/tests/test_owheatmap.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313
from Orange.widgets.tests.base import WidgetTest, WidgetOutputsTestMixin, datasets
1414

1515

16+
def image_row_colors(image):
17+
colors = np.full((image.height(), 3), np.nan)
18+
for r in range(image.height()):
19+
c = image.pixelColor(0, r)
20+
colors[r] = c.red(), c.green(), c.blue()
21+
return colors
22+
23+
1624
class TestOWHeatMap(WidgetTest, WidgetOutputsTestMixin):
1725
@classmethod
1826
def setUpClass(cls):
@@ -164,10 +172,7 @@ def test_use_enough_colors(self):
164172
self.widget.update_color_schema()
165173
heatmap_widget = self.widget.heatmap_widget_grid[0][0]
166174
image = heatmap_widget.heatmap_item.pixmap().toImage()
167-
colors = np.full((len(data), 3), np.nan)
168-
for r in range(len(data)):
169-
c = image.pixelColor(0, r)
170-
colors[r] = c.red(), c.green(), c.blue()
175+
colors = image_row_colors(image)
171176
unique_colors = len(np.unique(colors, axis=0))
172177
self.assertLessEqual(len(data)*self.widget.threshold_low, unique_colors)
173178

@@ -198,6 +203,29 @@ def test_saved_selection(self):
198203
self.send_signal(w.Inputs.data, iris, widget=w)
199204
self.assertEqual(len(self.get_output(w.Outputs.selected_data)), 21)
200205

206+
def test_center_palette(self):
207+
data = np.arange(2).reshape(-1, 1)
208+
table = Table.from_numpy(Domain([ContinuousVariable("y")]), data)
209+
self.send_signal(self.widget.Inputs.data, table)
210+
211+
cb_model = self.widget.color_cb.model()
212+
ind = cb_model.indexFromItem(cb_model.findItems("Green-Black-Red")[0]).row()
213+
self.widget.palette_index = ind
214+
215+
desired_uncentered = [[0, 255, 0],
216+
[255, 0, 0]]
217+
218+
desired_centered = [[0, 0, 0],
219+
[255, 0, 0]]
220+
221+
for center, desired in [(False, desired_uncentered), (True, desired_centered)]:
222+
self.widget.center_palette = center
223+
self.widget.update_color_schema()
224+
heatmap_widget = self.widget.heatmap_widget_grid[0][0]
225+
image = heatmap_widget.heatmap_item.pixmap().toImage()
226+
colors = image_row_colors(image)
227+
np.testing.assert_almost_equal(colors, desired)
228+
201229

202230
if __name__ == "__main__":
203231
unittest.main()

0 commit comments

Comments
 (0)