Skip to content

Commit

Permalink
Parameter Fitter: Retain settings on input
Browse files Browse the repository at this point in the history
  • Loading branch information
VesnaT committed Nov 12, 2024
1 parent 8376e3b commit 25bd989
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 89 deletions.
6 changes: 3 additions & 3 deletions Orange/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from collections.abc import Iterable
import re
import warnings
from typing import Callable, Optional, NamedTuple, Union, Type
from typing import Callable, Optional, NamedTuple, Type

import numpy as np
import scipy
Expand Down Expand Up @@ -186,8 +186,8 @@ def active_preprocessors(self):
self.preprocessors is not type(self).preprocessors):
yield from type(self).preprocessors

# declared for derived classes, pylint: disable=unused-argument
def fitted_parameters(self, problem_type: Union[str, Table, Domain]) -> list:
@property
def fitted_parameters(self) -> list:
return []

# pylint: disable=no-self-use
Expand Down
6 changes: 1 addition & 5 deletions Orange/classification/random_forest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sklearn.ensemble as skl_ensemble

from Orange.base import RandomForestModel, Learner
from Orange.base import RandomForestModel
from Orange.classification import SklLearner, SklModel
from Orange.classification.tree import SklTreeClassifier
from Orange.data import Variable, DiscreteVariable
Expand Down Expand Up @@ -58,7 +58,3 @@ def __init__(self,
preprocessors=None):
super().__init__(preprocessors=preprocessors)
self.params = vars()

def fitted_parameters(self, _) -> list[Learner.FittedParameter]:
return [self.FittedParameter("n_estimators", "Number of trees",
int, 1, None)]
1 change: 1 addition & 0 deletions Orange/evaluation/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def __init__(self, data=None, *,
row_indices=None, folds=None, score_by_folds=True,
learners=None, models=None, failed=None,
actual=None, predicted=None, probabilities=None,
# pylint: disable=unused-argument
store_data=None, store_models=None,
train_time=None, test_time=None):
"""
Expand Down
13 changes: 5 additions & 8 deletions Orange/modelling/randomforest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from typing import Union

from Orange.base import RandomForestModel, Learner
from Orange.classification import RandomForestLearner as RFClassification
from Orange.data import Variable, Domain, Table
from Orange.data import Variable
from Orange.modelling import SklFitter
from Orange.preprocess.score import LearnerScorer
from Orange.regression import RandomForestRegressionLearner as RFRegression
Expand All @@ -27,8 +25,7 @@ class RandomForestLearner(SklFitter, _FeatureScorerMixin):

__returns__ = RandomForestModel

def fitted_parameters(
self,
problem_type: Union[str, Table, Domain]
) -> list[Learner.FittedParameter]:
return self.get_learner(problem_type).fitted_parameters(problem_type)
@property
def fitted_parameters(self) -> list[Learner.FittedParameter]:
return [self.FittedParameter("n_estimators", "Number of trees",
int, 1, None)]
3 changes: 2 additions & 1 deletion Orange/regression/pls.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@ def incompatibility_reason(self, domain):
reason = "Only numeric target variables expected."
return reason

def fitted_parameters(self, _) -> list[Learner.FittedParameter]:
@property
def fitted_parameters(self) -> list[Learner.FittedParameter]:
return [self.FittedParameter("n_components", "Components",
int, 1, None)]

Expand Down
6 changes: 1 addition & 5 deletions Orange/regression/random_forest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sklearn.ensemble as skl_ensemble

from Orange.base import RandomForestModel, Learner
from Orange.base import RandomForestModel
from Orange.data import Variable, ContinuousVariable
from Orange.preprocess.score import LearnerScorer
from Orange.regression import SklLearner, SklModel
Expand Down Expand Up @@ -57,7 +57,3 @@ def __init__(self,
preprocessors=None):
super().__init__(preprocessors=preprocessors)
self.params = vars()

def fitted_parameters(self, _) -> list[Learner.FittedParameter]:
return [self.FittedParameter("n_estimators", "Number of trees",
int, 1, None)]
2 changes: 1 addition & 1 deletion Orange/regression/tests/test_pls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def table(rows, attr, variables):

class TestPLSRegressionLearner(unittest.TestCase):
def test_fitted_parameters(self):
fitted_parameters = PLSRegressionLearner().fitted_parameters(None)
fitted_parameters = PLSRegressionLearner().fitted_parameters
self.assertIsInstance(fitted_parameters, list)
self.assertEqual(len(fitted_parameters), 1)

Expand Down
95 changes: 47 additions & 48 deletions Orange/widgets/evaluate/owparameterfitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,12 @@ class Inputs:
DEFAULT_PARAMETER_INDEX = 0
DEFAULT_MINIMUM = 1
DEFAULT_MAXIMUM = 9
parameter_index = Setting(DEFAULT_PARAMETER_INDEX, schema_only=True)
parameter_index = Setting(DEFAULT_PARAMETER_INDEX)
FROM_RANGE, MANUAL = range(2)
type: int = Setting(FROM_RANGE)
minimum: int = Setting(DEFAULT_MINIMUM, schema_only=True)
maximum: int = Setting(DEFAULT_MAXIMUM, schema_only=True)
manual_steps: str = Setting("", schema_only=True)
minimum: int = Setting(DEFAULT_MINIMUM)
maximum: int = Setting(DEFAULT_MAXIMUM)
manual_steps: str = Setting("")
auto_commit = Setting(True)

class Error(OWWidget.Error):
Expand All @@ -345,13 +345,10 @@ def __init__(self):
self._data: Optional[Table] = None
self._learner: Optional[Learner] = None
self.__parameters_model = QStandardItemModel()

self.__pending_parameter_index = self.parameter_index \
if self.parameter_index != self.DEFAULT_PARAMETER_INDEX else None
self.__pending_minimum = self.minimum \
if self.minimum != self.DEFAULT_MINIMUM else None
self.__pending_maximum = self.maximum \
if self.maximum != self.DEFAULT_MAXIMUM else None
self.__initialize_settings = \
self.parameter_index == self.DEFAULT_PARAMETER_INDEX and \
self.minimum == self.DEFAULT_MINIMUM and \
self.maximum == self.DEFAULT_MAXIMUM

self.setup_gui()
VisualSettingsDialog(
Expand Down Expand Up @@ -418,10 +415,13 @@ def _():

gui.auto_apply(self.buttonsArea, self, "auto_commit")

self._update_preview()

def __on_type_changed(self):
self._settings_changed()

def __on_parameter_changed(self):
self.__initialize_settings = True
self._set_range_controls()
self._settings_changed()

Expand All @@ -439,19 +439,16 @@ def _settings_changed(self):

@property
def fitted_parameters(self) -> list:
if not self._learner \
or isinstance(self._learner, Fitter) and not self._data:
if not self._learner:
return []
return self._learner.fitted_parameters(self._data)
return self._learner.fitted_parameters

@property
def initial_parameters(self) -> dict:
if not self._learner:
return {}
if isinstance(self._learner, Fitter):
if not self._data:
return {}
return self._learner.get_params(self._data)
return self._learner.get_params(self._data or "classification")
return self._learner.params

@property
Expand Down Expand Up @@ -495,38 +492,32 @@ def _steps_from_manual(self) -> tuple[int, ...]:
@Inputs.data
@check_multiple_targets_input
def set_data(self, data: Optional[Table]):
self.Error.not_enough_data.clear()
self.Error.missing_target.clear()
self._data = data
if self._data and len(self._data) < N_FOLD:
self.Error.not_enough_data()
self._data = None
if self._data and len(self._data.domain.class_vars) < 1:
self.Error.missing_target()
self._data = None

@Inputs.learner
def set_learner(self, learner: Optional[Learner]):
if self._learner:
self.__initialize_settings = \
not isinstance(self._learner, type(learner))
self._learner = learner

def handleNewSignals(self):
self.Warning.clear()
self.Error.unknown_err.clear()
self.Error.not_enough_data.clear()
self.Error.incompatible_learner.clear()
self.Error.manual_steps_error.clear()
self.Error.min_max_error.clear()
self.Error.missing_target.clear()
self.clear()

if self._data is None or self._learner is None:
return

if self._data and len(self._data) < N_FOLD:
self.Error.not_enough_data()
self._data = None
return

if self._data and len(self._data.domain.class_vars) < 1:
self.Error.missing_target()
self._data = None
return

reason = self._learner.incompatibility_reason(self._data.domain)
if reason:
self.Error.incompatible_learner(reason)
if self._learner is None:
return

for param in self.fitted_parameters:
Expand All @@ -535,19 +526,22 @@ def handleNewSignals(self):
if not self.fitted_parameters:
self.Warning.no_parameters(self._learner.name)
else:
if self.__pending_parameter_index is not None:
self.parameter_index = self.__pending_parameter_index
if self.__initialize_settings:
self.parameter_index = 0
else:
self.__combo.setCurrentIndex(self.parameter_index)
self.__pending_parameter_index = None
self._set_range_controls()
if self.__pending_minimum is not None:
self.minimum = self.__pending_minimum
self.__pending_minimum = None
if self.__pending_maximum is not None:
self.maximum = self.__pending_maximum
self.__pending_maximum = None

self._update_preview()

if self._data is None:
return

reason = self._learner.incompatibility_reason(self._data.domain)
if reason:
self.Error.incompatible_learner(reason)
return

self.commit.now()

def _set_range_controls(self):
Expand All @@ -561,19 +555,24 @@ def _set_range_controls(self):
if param.min is not None:
self.__spin_min.setMinimum(param.min)
self.__spin_max.setMinimum(param.min)
self.minimum = param.min
if self.__initialize_settings:
self.minimum = param.min
else:
self.__spin_min.setMinimum(-MIN_MAX_SPIN)
self.__spin_max.setMinimum(-MIN_MAX_SPIN)
self.minimum = self.initial_parameters[param.name]
if self.__initialize_settings:
self.minimum = self.initial_parameters[param.name]
if param.max is not None:
self.__spin_min.setMaximum(param.max)
self.__spin_max.setMaximum(param.max)
self.maximum = param.max
if self.__initialize_settings:
self.maximum = param.max
else:
self.__spin_min.setMaximum(MIN_MAX_SPIN)
self.__spin_max.setMaximum(MIN_MAX_SPIN)
self.maximum = self.initial_parameters[param.name]
if self.__initialize_settings:
self.maximum = self.initial_parameters[param.name]
self.__initialize_settings = False

tip = "Enter a list of values"
if param.min is not None:
Expand Down
Loading

0 comments on commit 25bd989

Please sign in to comment.