1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
from itertools import chain
from AnyQt.QtCore import Qt
from AnyQt.QtWidgets import QLayout, QSizePolicy
from Orange.data import Table, Domain, ContinuousVariable, StringVariable
from Orange.regression.linear import (
LassoRegressionLearner, LinearRegressionLearner,
RidgeRegressionLearner, ElasticNetLearner
)
from Orange.widgets import settings, gui
from Orange.widgets.utils.owlearnerwidget import OWBaseLearner
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import Output
class OWLinearRegression(OWBaseLearner):
name = "Linear Regression"
description = "A linear regression algorithm with optional L1 (LASSO), " \
"L2 (ridge) or L1L2 (elastic net) regularization."
icon = "icons/LinearRegression.svg"
replaces = [
"Orange.widgets.regression.owlinearregression.OWLinearRegression",
]
priority = 60
keywords = "linear regression, ridge, lasso, elastic net"
LEARNER = LinearRegressionLearner
class Outputs(OWBaseLearner.Outputs):
coefficients = Output("Coefficients", Table, explicit=True)
#: Types
REGULARIZATION_TYPES = ["No regularization", "Ridge regression (L2)",
"Lasso regression (L1)", "Elastic net regression"]
OLS, Ridge, Lasso, Elastic = 0, 1, 2, 3
ridge = settings.Setting(False)
reg_type = settings.Setting(OLS)
alpha_index = settings.Setting(0)
l2_ratio = settings.Setting(0.5)
fit_intercept = settings.Setting(True)
autosend = settings.Setting(True)
alphas = list(chain([x / 10000 for x in range(1, 10)],
[x / 1000 for x in range(1, 20)],
[x / 100 for x in range(2, 20)],
[x / 10 for x in range(2, 9)],
range(1, 20),
range(20, 100, 5),
range(100, 1001, 100)))
def add_main_layout(self):
# this is part of init, pylint: disable=attribute-defined-outside-init
box = gui.hBox(self.controlArea, "Parameters")
gui.checkBox(box, self, "fit_intercept",
"Fit intercept (unchecking it fixes it to zero)",
callback=self._intercept_changed)
box = gui.hBox(self.controlArea, "Regularization")
gui.radioButtons(box, self, "reg_type",
btnLabels=self.REGULARIZATION_TYPES,
callback=self._reg_type_changed)
self.alpha_box = box2 = gui.vBox(box, margin=10)
gui.widgetLabel(box2, "Regularization strength:")
gui.hSlider(
box2, self, "alpha_index",
minValue=0, maxValue=len(self.alphas) - 1,
callback=self._alpha_changed, createLabel=False)
box3 = gui.hBox(box2)
box3.layout().setAlignment(Qt.AlignCenter)
self.alpha_label = gui.widgetLabel(box3, "")
self._set_alpha_label()
box4 = gui.vBox(box2, margin=0)
gui.widgetLabel(box4, "Elastic net mixing:")
box5 = gui.hBox(box4)
gui.widgetLabel(box5, "L1")
self.l2_ratio_slider = gui.hSlider(
box5, self, "l2_ratio", minValue=0.01, maxValue=0.99,
intOnly=False, createLabel=False, width=120,
step=0.01, callback=self._l2_ratio_changed)
gui.widgetLabel(box5, "L2")
self.l2_ratio_label = gui.widgetLabel(
box4, "",
sizePolicy=(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed))
self.l2_ratio_label.setAlignment(Qt.AlignCenter)
box5 = gui.hBox(self.controlArea)
box5.layout().setAlignment(Qt.AlignCenter)
self._set_l2_ratio_label()
self.layout().setSizeConstraint(QLayout.SetFixedSize)
self.controls.alpha_index.setEnabled(self.reg_type != self.OLS)
self.l2_ratio_slider.setEnabled(self.reg_type == self.Elastic)
def _intercept_changed(self):
self.apply()
def _reg_type_changed(self):
self.controls.alpha_index.setEnabled(self.reg_type != self.OLS)
self.l2_ratio_slider.setEnabled(self.reg_type == self.Elastic)
self.apply()
def _set_alpha_label(self):
self.alpha_label.setText("Alpha: {}".format(self.alphas[self.alpha_index]))
def _alpha_changed(self):
self._set_alpha_label()
self.apply()
def _set_l2_ratio_label(self):
self.l2_ratio_label.setText(
"{:.{}f} : {:.{}f}".format(1 - self.l2_ratio, 2, self.l2_ratio, 2))
def _l2_ratio_changed(self):
self._set_l2_ratio_label()
self.apply()
def create_learner(self):
alpha = self.alphas[self.alpha_index]
preprocessors = self.preprocessors
args = dict(preprocessors=preprocessors,
fit_intercept=self.fit_intercept)
if self.reg_type == OWLinearRegression.OLS:
learner = LinearRegressionLearner(**args)
elif self.reg_type == OWLinearRegression.Ridge:
learner = RidgeRegressionLearner(alpha=alpha, **args)
elif self.reg_type == OWLinearRegression.Lasso:
learner = LassoRegressionLearner(alpha=alpha, **args)
elif self.reg_type == OWLinearRegression.Elastic:
learner = ElasticNetLearner(alpha=alpha,
l1_ratio=1 - self.l2_ratio, **args)
return learner
def update_model(self):
super().update_model()
coef_table = None
if self.model is not None:
domain = Domain(
[ContinuousVariable("coef")], metas=[StringVariable("name")])
coefs = list(self.model.coefficients)
names = [attr.name for attr in self.model.domain.attributes]
if self.fit_intercept:
coefs.insert(0, self.model.intercept)
names.insert(0, "intercept")
coef_table = Table.from_list(domain, list(zip(coefs, names)))
coef_table.name = "coefficients"
self.Outputs.coefficients.send(coef_table)
def get_learner_parameters(self):
regularization = "No Regularization"
if self.reg_type == OWLinearRegression.Ridge:
regularization = ("Ridge Regression (L2) with α={}"
.format(self.alphas[self.alpha_index]))
elif self.reg_type == OWLinearRegression.Lasso:
regularization = ("Lasso Regression (L1) with α={}"
.format(self.alphas[self.alpha_index]))
elif self.reg_type == OWLinearRegression.Elastic:
regularization = ("Elastic Net Regression with α={}"
" and L1:L2 ratio of {}:{}"
.format(self.alphas[self.alpha_index],
self.l2_ratio,
1 - self.l2_ratio))
return (
("Regularization", regularization),
("Fit intercept", ["No", "Yes"][self.fit_intercept])
)
if __name__ == "__main__": # pragma: no cover
WidgetPreview(OWLinearRegression).run(Table("housing"))
|