Skip to content

Commit 6d5f0ac

Browse files
committed
Score: Name scorers and use names in ScoreTable
1 parent 73bd57d commit 6d5f0ac

File tree

4 files changed

+46
-16
lines changed

4 files changed

+46
-16
lines changed

Orange/evaluation/scoring.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def __new__(mcs, name, bases, dict_, **kwargs):
3737
if not kwargs.get("abstract"):
3838
# Don't use inherited names, look into dict_
3939
cls.name = dict_.get("name", name)
40+
cls.long_name = dict_.get("long_name", cls.name)
4041
cls.registry[name] = cls
4142
else:
4243
cls.registry = {}
@@ -139,6 +140,7 @@ def is_compatible(domain: Domain) -> bool:
139140
# pylint: disable=invalid-name
140141
class CA(ClassificationScore):
141142
__wraps__ = skl_metrics.accuracy_score
143+
name = "CA"
142144
long_name = "Classification accuracy"
143145
priority = 20
144146

@@ -189,16 +191,20 @@ def compute_score(self, results, target=None, average='binary'):
189191

190192
class Precision(TargetScore):
191193
__wraps__ = skl_metrics.precision_score
194+
name = "Prec"
195+
long_name = "Precision"
192196
priority = 40
193197

194198

195199
class Recall(TargetScore):
196200
__wraps__ = skl_metrics.recall_score
201+
name = long_name = "Recall"
197202
priority = 50
198203

199204

200205
class F1(TargetScore):
201206
__wraps__ = skl_metrics.f1_score
207+
name = long_name = "F1"
202208
priority = 30
203209

204210

@@ -217,6 +223,7 @@ class AUC(ClassificationScore):
217223
__wraps__ = skl_metrics.roc_auc_score
218224
separate_folds = True
219225
is_binary = True
226+
name = "AUC"
220227
long_name = "Area under ROC curve"
221228
priority = 10
222229

@@ -291,6 +298,8 @@ class LogLoss(ClassificationScore):
291298
"""
292299
__wraps__ = skl_metrics.log_loss
293300
priority = 120
301+
name = "LogLoss"
302+
long_name = "Logistic loss"
294303
default_visible = False
295304

296305
def compute_score(self, results, eps=1e-15, normalize=True,
@@ -308,6 +317,8 @@ def compute_score(self, results, eps=1e-15, normalize=True,
308317
class Specificity(ClassificationScore):
309318
is_binary = True
310319
priority = 110
320+
name = "Spec"
321+
long_name = "Specificity"
311322
default_visible = False
312323

313324
@staticmethod
@@ -361,11 +372,13 @@ def compute_score(self, results, target=None, average="binary"):
361372

362373
class MSE(RegressionScore):
363374
__wraps__ = skl_metrics.mean_squared_error
375+
name = "MSE"
364376
long_name = "Mean square error"
365377
priority = 20
366378

367379

368380
class RMSE(RegressionScore):
381+
name = "RMSE"
369382
long_name = "Root mean square error"
370383

371384
def compute_score(self, results):
@@ -375,18 +388,21 @@ def compute_score(self, results):
375388

376389
class MAE(RegressionScore):
377390
__wraps__ = skl_metrics.mean_absolute_error
391+
name = "MAE"
378392
long_name = "Mean absolute error"
379393
priority = 40
380394

381395

382396
# pylint: disable=invalid-name
383397
class R2(RegressionScore):
384398
__wraps__ = skl_metrics.r2_score
399+
name = "R2"
385400
long_name = "Coefficient of determination"
386401
priority = 50
387402

388403

389404
class CVRMSE(RegressionScore):
405+
name = "CVRMSE"
390406
long_name = "Coefficient of variation of the RMSE"
391407
priority = 110
392408
default_visible = False

Orange/widgets/evaluate/owtestandscore.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ def update_stats_model(self):
655655
item.setData(float(stat.value[0]), Qt.DisplayRole)
656656
else:
657657
item.setToolTip(str(stat.exception))
658+
# pylint: disable=unsubscriptable-object
658659
if self.score_table.show_score_hints[scorer.__name__]:
659660
has_missing_scores = True
660661
row.append(item)

Orange/widgets/evaluate/tests/test_utils.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import unittest
44
import collections
55
from distutils.version import LooseVersion
6+
from itertools import count
67
from unittest.mock import patch
78

89
import numpy as np
@@ -12,7 +13,7 @@
1213
from AnyQt.QtCore import QPoint, Qt
1314

1415
import Orange
15-
from Orange.evaluation.scoring import Score, RMSE, AUC, CA, F1, Specificity
16+
from Orange.evaluation.scoring import Score, AUC, CA, F1, Specificity
1617
from Orange.widgets.evaluate.utils import ScoreTable, usable_scorers
1718
from Orange.widgets.tests.base import GuiTest
1819
from Orange.data import Table, DiscreteVariable, ContinuousVariable
@@ -47,12 +48,12 @@ def setUp(self):
4748
class NewScore(Score):
4849
name = "new score"
4950

51+
self.NewScore = NewScore # pylint: disable=invalid-name
52+
5053
self.orig_hints = ScoreTable.show_score_hints
5154
hints = ScoreTable.show_score_hints = self.orig_hints.default.copy()
5255
hints.update(dict(F1=True, CA=False, AUC=True, Recall=True,
5356
Specificity=False, NewScore=True))
54-
self.name_to_qualname = {score.name: score.__name__
55-
for score in Score.registry.values()}
5657
self.score_table = ScoreTable(None)
5758
self.score_table.update_header([F1, CA, AUC, Specificity, NewScore])
5859
self.score_table._update_shown_columns()
@@ -72,23 +73,28 @@ def addAction(menu, a):
7273
return action
7374

7475
def execmenu(*_):
75-
scores = ["F1", "CA", "AUC", "Specificity", "new score"]
76-
self.assertEqual(list(actions)[2:], scores)
76+
# pylint: disable=unsubscriptable-object,unsupported-assignment-operation
77+
scorers = [F1, CA, AUC, Specificity, self.NewScore]
78+
self.assertEqual(list(actions)[2:], ['F1',
79+
'Classification accuracy (CA)',
80+
'Area under ROC curve (AUC)',
81+
'Specificity (Spec)',
82+
'new score'])
7783
header = self.score_table.view.horizontalHeader()
78-
for i, (name, action) in enumerate(actions.items()):
84+
for i, action, scorer in zip(count(), actions.values(), scorers):
7985
if i >= 2:
8086
self.assertEqual(action.isChecked(),
81-
hints[self.name_to_qualname[name]],
82-
msg=f"error in section {name}")
87+
hints[scorer.__name__],
88+
msg=f"error in section {scorer.name}")
8389
self.assertEqual(header.isSectionHidden(i),
84-
hints[self.name_to_qualname[name]],
85-
msg=f"error in section {name}")
86-
actions["CA"].triggered.emit(True)
90+
hints[scorer.__name__],
91+
msg=f"error in section {scorer.name}")
92+
actions["Classification accuracy (CA)"].triggered.emit(True)
8793
hints["CA"] = True
8894
for k, v in hints.items():
8995
self.assertEqual(self.score_table.show_score_hints[k], v,
9096
msg=f"error at {k}")
91-
actions["AUC"].triggered.emit(False)
97+
actions["Area under ROC curve (AUC)"].triggered.emit(False)
9298
hints["AUC"] = False
9399
for k, v in hints.items():
94100
self.assertEqual(self.score_table.show_score_hints[k], v,
@@ -99,8 +105,8 @@ def execmenu(*_):
99105
# Assertions are made within `menuexec` since they check the
100106
# instances of `QAction`, which are invalid (destroyed by Qt?) after
101107
# `menuexec` finishes.
102-
with unittest.mock.patch("AnyQt.QtWidgets.QMenu.addAction", addAction), \
103-
unittest.mock.patch("AnyQt.QtWidgets.QMenu.exec", execmenu):
108+
with patch("AnyQt.QtWidgets.QMenu.addAction", addAction), \
109+
patch("AnyQt.QtWidgets.QMenu.exec", execmenu):
104110
self.score_table.show_column_chooser(QPoint(0, 0))
105111

106112
def test_sorting(self):

Orange/widgets/evaluate/utils.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,16 @@ def show_column_chooser(self, pos):
184184
header = self.view.horizontalHeader()
185185
for col in range(1, self.model.columnCount()):
186186
item = self.model.horizontalHeaderItem(col)
187-
action = menu.addAction(item.data(Qt.DisplayRole))
188-
action.setCheckable(True)
189187
qualname = item.data(Qt.UserRole)
188+
if col < 3:
189+
option = item.data(Qt.DisplayRole)
190+
else:
191+
score = Score.registry[qualname]
192+
option = score.long_name
193+
if score.name != score.long_name:
194+
option += f" ({score.name})"
195+
action = menu.addAction(option)
196+
action.setCheckable(True)
190197
action.setChecked(self.show_score_hints[qualname])
191198

192199
@action.triggered.connect

0 commit comments

Comments
 (0)