Repository: biolab/orange3

Issue:
Confusion Matrix creates non-unique names in annotated data output
I am trying to use confustion matrix as an outlier stripper.

I have a chain setup like this File to select rows to select columns to outliers to data sampler to preprocessor to test and score with learners attached. Then to my first confusion matrix, I send data from there to the  second test and score with learners attached. The second test and score has a second confusion matrix, which breaks down 99% of the time, with the final error being that the variable names have to be unique in a domain. 

My approach is genius it does not improved scores, but the 'specific generality' it gives is what I am experimenting with.

<!-- 
Thanks for taking the time to report a bug!
If you're raising an issue about an add-on (i.e., installed via Options > Add-ons), raise an issue in the relevant add-on's issue tracker instead. See: https://github.com/biolab?q=orange3
To fix the bug, we need to be able to reproduce it. Please answer the following questions to the best of your ability.
-->


**What's wrong?**
<!-- Be specific, clear, and concise. Include screenshots if relevant. -->
<!-- If you're getting an error message, copy it, and enclose it with three backticks (```). -->





**How can we reproduce the problem?** 
<!-- Upload a zip with the .ows file and data. -->
<!-- Describe the steps (open this widget, click there, then add this...) -->





**What's your environment?**
<!-- To find your Orange version, see "Help → About → Version" or `Orange.version.full_version` in code -->
- Operating system:
- Orange version:
- How you installed Orange:



Discussion:
Please attach a workflow with the error and describe how you installed Orange (which version) and which OS you are using.
@ajdapretnar is correct: if you don't send us the workflow, we cannot know that you have also checked the "Probabilities" checkbox in Confusion Matrix, which is essential for reproducing this problem.

Well, we *cannot know* unless we can *guess* what is going on. The bug is here: https://github.com/biolab/orange3/blob/master/Orange/widgets/evaluate/owconfusionmatrix.py#L387
Orange is installed through anaconda, it is the latest version which is 3.30.2 and yes the probabilities ticked is when it doesn't work.

Thanks in advanced. 

Resolving patch:
diff --git a/Orange/widgets/evaluate/owconfusionmatrix.py b/Orange/widgets/evaluate/owconfusionmatrix.py
--- a/Orange/widgets/evaluate/owconfusionmatrix.py
+++ b/Orange/widgets/evaluate/owconfusionmatrix.py
@@ -384,9 +384,9 @@ def _prepare_data(self):
                         self.results.probabilities is not None:
             probs = self.results.probabilities[self.selected_learner[0]]
             extra.append(np.array(probs, dtype=object))
-            pvars = [Orange.data.ContinuousVariable("p({})".format(value))
-                     for value in class_var.values]
-            metas = metas + tuple(pvars)
+            names = [f"p({value})" for value in class_var.values]
+            names = get_unique_names(self.data.domain, names)
+            metas += tuple(map(Orange.data.ContinuousVariable, names))
 
         domain = Orange.data.Domain(self.data.domain.attributes,
                                     self.data.domain.class_vars,


Test patch:
diff --git a/Orange/widgets/evaluate/tests/test_owconfusionmatrix.py b/Orange/widgets/evaluate/tests/test_owconfusionmatrix.py
--- a/Orange/widgets/evaluate/tests/test_owconfusionmatrix.py
+++ b/Orange/widgets/evaluate/tests/test_owconfusionmatrix.py
@@ -1,7 +1,8 @@
 # pylint: disable=missing-docstring, protected-access
+import unittest
 import numpy as np
 
-from Orange.data import Table
+from Orange.data import Table, Domain
 from Orange.classification import NaiveBayesLearner, TreeLearner
 from Orange.regression import MeanLearner
 from Orange.evaluation.testing import CrossValidation, TestOnTrainingData, \
@@ -125,3 +126,33 @@ def test_unique_output_domain(self):
         self.send_signal(self.widget.Inputs.evaluation_results, input_data)
         output = self.get_output(self.widget.Outputs.annotated_data)
         self.assertEqual(output.domain.metas[0].name, 'iris(Learner #1) (1)')
+
+    def test_unique_var_names(self):
+        bayes = NaiveBayesLearner()
+        domain = self.iris.domain
+        results = CrossValidation(k=3, store_data=True)(self.iris, [bayes])
+        self.widget.append_probabilities = True
+        self.widget.append_predictions = True
+        self.send_signal(self.widget.Inputs.evaluation_results, results)
+
+        out_data = self.get_output(self.widget.Outputs.annotated_data)
+
+        widget2 = self.create_widget(OWConfusionMatrix)
+        data2 = out_data.transform(
+            Domain(domain.attributes, domain.class_vars,
+                   [meta for meta in out_data.domain.metas if "versicolor" not in meta.name]))
+        results2 = CrossValidation(k=3, store_data=True)(data2, [bayes])
+        widget2.append_probabilities = True
+        widget2.append_predictions = True
+        self.send_signal(widget2.Inputs.evaluation_results, results2)
+        out_data2 = self.get_output(widget2.Outputs.annotated_data)
+        self.assertEqual({meta.name for meta in out_data2.domain.metas},
+                         {'Selected', 'Selected (1)',
+                          'iris(Learner #1)', 'iris(Learner #1) (1)',
+                          'p(Iris-setosa)', 'p(Iris-virginica)',
+                          'p(Iris-setosa) (1)', 'p(Iris-versicolor) (1)',
+                          'p(Iris-virginica) (1)'})
+
+
+if __name__ == "__main__":
+    unittest.main()