Repository: biolab/orange3

Issue:
Report for Stochastic Gradient Regression
Implement Report function in Stochastic Gradient Regression as in other widgets. Report on learner name, model parameters and data.



Discussion:
issue almost fixed,will send PR soon.

I am facing one problem here. When I open the Stochastic Gradient Regression widget, it doesn't fit on my laptop and I can't see the `Apply` button on the screen.
How to fit the widget to the screen or make the widget scrollable?


Resolving patch:
diff --git a/Orange/widgets/regression/owsgdregression.py b/Orange/widgets/regression/owsgdregression.py
--- a/Orange/widgets/regression/owsgdregression.py
+++ b/Orange/widgets/regression/owsgdregression.py
@@ -1,8 +1,7 @@
 # -*- coding: utf-8 -*-
+from collections import OrderedDict
 
 from PyQt4 import QtGui
-from PyQt4.QtGui import QGridLayout, QLabel
-from PyQt4.QtCore import Qt
 
 from Orange.data import Table
 from Orange.regression.linear import SGDRegressionLearner, LinearModel
@@ -25,25 +24,31 @@ class OWSGDRegression(OWProvidesLearner, widget.OWWidget):
     learner_name = settings.Setting("SGD Regression")
 
     alpha = settings.Setting(0.0001)
-    #: epsilon parameter for Epsilon SVR
     epsilon = settings.Setting(0.1)
     eta0 = settings.Setting(0.01)
     l1_ratio = settings.Setting(0.15)
     power_t = settings.Setting(0.25)
     n_iter = settings.Setting(5)
-
-    #: Loss of function to be used
     SqLoss, Huber, Epsilon_i, SqEpsilon_i = 0, 1, 2, 3
     L1, L2, ElasticNet = 0, 1, 2
-    #: Selected loss of function
     loss_function = settings.Setting(SqLoss)
     penalty_type = settings.Setting(L2)
-    InvScaling, Constant = 0, 1
+    Constant, InvScaling = 0, 1
     learning_rate = settings.Setting(InvScaling)
 
     want_main_area = False
     resizing_enabled = False
 
+    LOSS_FUNCTIONS = ["Squared loss",
+                      "Huber",
+                      "Epsilon insensitive",
+                      "Squared epsilon insensitive"]
+    PENALTIES = ["Absolute norm (L1)",
+                 "Euclidean norm (L2)",
+                 "Elastic net (L1 and L2)"]
+    LEARNING_RATES = ["Constant",
+                      "Inverse scaling"]
+
     def __init__(self):
         super().__init__()
 
@@ -53,82 +58,58 @@ def __init__(self):
         box = gui.widgetBox(self.controlArea, self.tr("Name"))
         gui.lineEdit(box, self, "learner_name")
 
-        form = QGridLayout()
-        typebox = gui.radioButtonsInBox(
-            self.controlArea, self, "lossfunc", [],
-            orientation=form,
-        )
-
-        # Loss function control
-        box = gui.widgetBox(self.controlArea, self.tr("Loss function to be used"))
-        buttonbox = gui.radioButtonsInBox(
-            box, self, "loss_function",
-            btnLabels=["Squared loss",
-                       "Huber",
-                       "Epsilon insensitive",
-                       "Squared Epsilon insensitive"],
-            callback=self._on_func_changed
-        )
-
-        parambox = gui.widgetBox(box, orientation="horizontal")
-
-        box = gui.widgetBox(self.controlArea, self.tr("Penalty"))
-        buttonbox = gui.radioButtonsInBox(
-            box, self, "penalty_type",
-            btnLabels=["Absolute norm (L1)",
-                       "Euclidean norm (L2)",
-                       "Elastic Net (both)"],
-            callback=self._on_penalty_changed
-        )
-
-        parambox = gui.widgetBox(box, orientation="horizontal")
-
-        box = gui.widgetBox(self.controlArea, self.tr("Learning rate"))
-        buttonbox = gui.radioButtonsInBox(
-            box, self, "learning_rate",
-            btnLabels=["Inverse scaling",
-                       "Constant"],
-            callback=self._on_lrate_changed
-        )
-
-
-
-        box = gui.widgetBox(self.controlArea, self.tr("Constants"))
-
-        form = QtGui.QFormLayout()
-        form.setContentsMargins(0, 0, 0, 0)
-
-        box.layout().addLayout(form)
-
-        alpha = gui.doubleSpin(box, self, "alpha", 0.0, 10.0, step=0.0001)
-        form.addRow("Alpha:", alpha)
-
-        spin = gui.doubleSpin(box, self, "eta0", 0.0, 10, step=0.01)
-        form.addRow("Eta0:", spin)
-
-        epsilon = gui.doubleSpin(box, self, "epsilon", 0.0, 10.0, step=0.01)
-        form.addRow("Epsilon:", epsilon)
-
-        l1_ratio = gui.doubleSpin(box, self, "l1_ratio", 0.0, 10.0, step=0.01)
+        def add_form(box):
+            gui.separator(box)
+            box2 = gui.hBox(box)
+            gui.rubber(box2)
+            form = QtGui.QFormLayout()
+            form.setContentsMargins(0, 0, 0, 0)
+            box2.layout().addLayout(form)
+            return form
+
+        box = gui.radioButtonsInBox(
+            self.controlArea, self, "loss_function", box="Loss function",
+            btnLabels=self.LOSS_FUNCTIONS, callback=self._on_func_changed)
+        form = add_form(box)
+        epsilon = gui.doubleSpin(
+            box, self, "epsilon", 0.0, 10.0, 0.01, controlWidth=70)
+        form.addRow("ε:", epsilon)
+
+        box = gui.radioButtons(
+            self.controlArea, self, "penalty_type", box="Penalty",
+            btnLabels=self.PENALTIES, callback=self._on_penalty_changed)
+        form = add_form(box)
+        alpha = gui.doubleSpin(
+            box, self, "alpha", 0.0, 10.0, 0.0001, controlWidth=70)
+        form.addRow("α:", alpha)
+        l1_ratio = gui.doubleSpin(
+            box, self, "l1_ratio", 0.0, 10.0, 0.01, controlWidth=70)
         form.addRow("L1 ratio:", l1_ratio)
 
-        power_t = gui.doubleSpin(box, self, "power_t", 0.0, 10.0, step=0.01)
+        box = gui.radioButtonsInBox(
+            self.controlArea, self, "learning_rate", box="Learning rate",
+            btnLabels=self.LEARNING_RATES, callback=self._on_lrate_changed)
+        form = add_form(box)
+        spin = gui.doubleSpin(
+            box, self, "eta0", 0.0, 10, 0.01, controlWidth=70)
+        form.addRow("η<sub>0</sub>:", spin)
+        power_t = gui.doubleSpin(
+            box, self, "power_t", 0.0, 10.0, 0.01, controlWidth=70)
         form.addRow("Power t:", power_t)
-
-
-        # Number of iterations control
-        box = gui.widgetBox(self.controlArea, "Number of iterations")
-        gui.doubleSpin(box, self, "n_iter", 0, 1e+6, step=1)
+        gui.separator(box, height=8)
+        niterations = gui.doubleSpin(
+            box, self, "n_iter", 0, 1e+6, 1, controlWidth=70)
+        form.addRow("Number of iterations:", niterations)
 
         self._func_params = [epsilon]
         self._penalty_params = [l1_ratio]
         self._lrate_params = [power_t]
 
-        gui.button(self.controlArea, self, "&Apply",
-                   callback=self.apply, default=True)
+        box = gui.hBox(self.controlArea)
+        box.layout().addWidget(self.report_button)
+        gui.button(box, self, "&Apply", callback=self.apply, default=True)
 
         self._on_func_changed()
-
         self.apply()
 
     @check_sql_input
@@ -139,9 +120,10 @@ def set_data(self, data):
             self.apply()
 
     def apply(self):
-        loss = ["squared_loss", "huber", "epsilon_insensitive", "squared_epsilon_insensitive"][self.loss_function]
+        loss = ["squared_loss", "huber", "epsilon_insensitive",
+                "squared_epsilon_insensitive"][self.loss_function]
         penalty = ["l1", "l2", "elasticnet"][self.penalty_type]
-        learning_rate = ["invscaling", "constant"][self.learning_rate]
+        learning_rate = ["constant", "invscaling"][self.learning_rate]
         common_args = dict(
             loss=loss,
             alpha=self.alpha,
@@ -180,6 +162,23 @@ def _on_func_changed(self):
         for spin, enabled in zip(self._func_params, mask):
             spin.setEnabled(enabled)
 
+    def send_report(self):
+        items = OrderedDict()
+        items['Loss function'] = self.LOSS_FUNCTIONS[self.loss_function]
+        if self.loss_function != self.SqLoss:
+            items['Loss function'] +=  ", ε={}".format(self.epsilon)
+        items['Penalty'] = self.PENALTIES[self.penalty_type]
+        if self.penalty_type == self.ElasticNet:
+            items['Penalty'] += ": L1 : L2 = {} : {}".format(
+                                self.l1_ratio, 1.0 - self.l1_ratio)
+        items['Penalty'] = items['Penalty'] + ', α={}'.format(self.alpha)
+        items['Learning rate'] = self.LEARNING_RATES[self.learning_rate]
+        items['Learning rate'] += ", η<sub>0</sub>={}".format(self.eta0)
+        if self.learning_rate == self.InvScaling:
+            items['Learning rate'] += ", power_t={}".format(self.power_t)
+        items['Number of iterations'] = self.n_iter
+        self.report_items("Model parameters", items)
+
     def _on_penalty_changed(self):
         enabled = [[False],  # l1
                    [False],  # l2
@@ -198,7 +197,6 @@ def _on_lrate_changed(self):
             spin.setEnabled(enabled)
 
 
-
 if __name__ == "__main__":
     import sys
     from PyQt4.QtGui import QApplication
@@ -210,4 +208,3 @@ def _on_lrate_changed(self):
     ow.show()
     a.exec_()
     ow.saveSettings()
-


Test patch: