Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a59288f
Parameter fitter: Add widget
VesnaT Oct 14, 2024
98b8035
Parameter Fitter: Add tooltip
VesnaT Oct 21, 2024
bf35a8f
Parameter Fitter: Support fitting # of trees in RF
VesnaT Oct 21, 2024
8055486
Parameter Fitter: Manual steps
VesnaT Oct 21, 2024
e38b011
Parameter Fitter: Assure some space below ticks
VesnaT Oct 21, 2024
d353372
Parameter Fitter: Tests
VesnaT Oct 23, 2024
493d5b5
Parameter Fitter: Report
VesnaT Oct 23, 2024
7785104
Parameter Fitter: Type annotations
VesnaT Oct 24, 2024
c57d2cd
Parameter Fitter: GUI changes, annotations
janezd Oct 25, 2024
26f3c6f
Parameter Fitter: Change icon
janezd Oct 26, 2024
f4791c5
Parameter Fitter: Fix send_report
VesnaT Oct 29, 2024
29a2541
Parameter Fitter: Fix minor glitches and annotations
janezd Oct 30, 2024
f9dcd02
Parameter Fitter: Check ranges for manual edit
janezd Nov 4, 2024
04d187d
Parameter Fitter: Slovenian translation
janezd Nov 4, 2024
272c124
Parameter Fitter: Lint
janezd Nov 4, 2024
e34d66d
Parameter Fitter: Allow dots in sequences
janezd Nov 5, 2024
041e242
Parameter Fitter: Replace variadic argument in fitted_parameters with…
janezd Nov 6, 2024
b30ddcb
Parameter Fitter: Renovate definition of FitterParameter
janezd Nov 6, 2024
b3dd2eb
Parameter Fitter: Add missing translations
janezd Nov 7, 2024
19bdabc
Parameter Fitter: Add documentation
janezd Nov 7, 2024
26cefa2
FittedParameter: Remove unused property
VesnaT Nov 8, 2024
d0c9c1b
PLS: Rename fitted parameter to 'Components'
VesnaT Nov 8, 2024
15baede
Parameter Fitter: Handle classless data
VesnaT Nov 8, 2024
3abcbed
Parameter Fitter: Handle multiclass data
VesnaT Nov 8, 2024
8376e3b
Parameter Fitter: Dont silence exceptions
VesnaT Nov 11, 2024
932fe14
Parameter Fitter: Retain settings on input
VesnaT Nov 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 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, Dict, Optional
from typing import Callable, Optional, NamedTuple, Type

import numpy as np
import scipy
Expand Down Expand Up @@ -88,6 +88,13 @@ class Learner(ReprableWithPreprocessors):
#: fitting the model
preprocessors = ()

class FittedParameter(NamedTuple):
name: str
label: str
type: Type
min: Optional[int] = None
max: Optional[int] = None

# Note: Do not use this class attribute.
# It remains here for compatibility reasons.
learner_adequacy_err_msg = ''
Expand Down Expand Up @@ -179,6 +186,10 @@ def active_preprocessors(self):
self.preprocessors is not type(self).preprocessors):
yield from type(self).preprocessors

@property
def fitted_parameters(self) -> list:
return []

# pylint: disable=no-self-use
def incompatibility_reason(self, _: Domain) -> Optional[str]:
"""Return None if a learner can fit domain or string explaining why it can not."""
Expand Down Expand Up @@ -883,5 +894,5 @@ def __init__(self, preprocessors=None, **kwargs):
self.params = kwargs

@SklLearner.params.setter
def params(self, values: Dict):
def params(self, values: dict):
self._params = values
24 changes: 17 additions & 7 deletions Orange/evaluation/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def _identity(x):


def _mp_worker(fold_i, train_data, test_data, learner_i, learner,
store_models):
store_models, suppresses_exceptions=True):
predicted, probs, model, failed = None, None, None, False
train_time, test_time = None, None
try:
Expand All @@ -45,6 +45,8 @@ def _mp_worker(fold_i, train_data, test_data, learner_i, learner,
test_time = time() - t0
# Different models can fail at any time raising any exception
except Exception as ex: # pylint: disable=broad-except
if not suppresses_exceptions:
raise ex
failed = ex
return _MpResults(fold_i, learner_i, store_models and model,
failed, len(test_data), predicted, probs,
Expand Down Expand Up @@ -96,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 Expand Up @@ -426,7 +429,8 @@ def fit(self, *args, **kwargs):
DeprecationWarning)
return self(*args, **kwargs)

def __call__(self, data, learners, preprocessor=None, *, callback=None):
def __call__(self, data, learners, preprocessor=None, *, callback=None,
suppresses_exceptions=True):
"""
Args:
data (Orange.data.Table): data to be used (usually split) into
Expand All @@ -435,6 +439,7 @@ def __call__(self, data, learners, preprocessor=None, *, callback=None):
preprocessor (Orange.preprocess.Preprocess): preprocessor applied
on training data
callback (Callable): a function called to notify about the progress
suppresses_exceptions (bool): suppress the exceptions if True

Returns:
results (Result): results of testing
Expand All @@ -457,7 +462,10 @@ def __call__(self, data, learners, preprocessor=None, *, callback=None):
part_results = []
parts = np.linspace(.0, .99, len(learners) * len(indices) + 1)[1:]
for progress, part in zip(parts, args_iter):
part_results.append(_mp_worker(*(part + ())))
part_results.append(
_mp_worker(*(part + ()),
suppresses_exceptions=suppresses_exceptions)
)
callback(progress)
callback(1)

Expand Down Expand Up @@ -723,7 +731,7 @@ def __new__(cls, data=None, test_data=None, learners=None,
test_data=test_data, **kwargs)

def __call__(self, data, test_data, learners, preprocessor=None,
*, callback=None):
*, callback=None, suppresses_exceptions=True):
"""
Args:
data (Orange.data.Table): training data
Expand All @@ -732,6 +740,7 @@ def __call__(self, data, test_data, learners, preprocessor=None,
preprocessor (Orange.preprocess.Preprocess): preprocessor applied
on training data
callback (Callable): a function called to notify about the progress
suppresses_exceptions (bool): suppress the exceptions if True

Returns:
results (Result): results of testing
Expand All @@ -746,7 +755,7 @@ def __call__(self, data, test_data, learners, preprocessor=None,
for (learner_i, learner) in enumerate(learners):
part_results.append(
_mp_worker(0, train_data, test_data, learner_i, learner,
self.store_models))
self.store_models, suppresses_exceptions))
callback((learner_i + 1) / len(learners))
callback(1)

Expand Down Expand Up @@ -778,13 +787,14 @@ def __new__(cls, data=None, learners=None, preprocessor=None, **kwargs):
**kwargs)

def __call__(self, data, learners, preprocessor=None, *, callback=None,
**kwargs):
suppresses_exceptions=True, **kwargs):
kwargs.setdefault("test_data", data)
# if kwargs contains anything besides test_data, this will be detected
# (and complained about) by super().__call__
return super().__call__(
data=data, learners=learners, preprocessor=preprocessor,
callback=callback, **kwargs)
callback=callback, suppresses_exceptions=suppresses_exceptions,
**kwargs)


def sample(table, n=0.7, stratified=False, replace=False,
Expand Down
7 changes: 6 additions & 1 deletion Orange/modelling/randomforest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from Orange.base import RandomForestModel
from Orange.base import RandomForestModel, Learner
from Orange.classification import RandomForestLearner as RFClassification
from Orange.data import Variable
from Orange.modelling import SklFitter
Expand All @@ -24,3 +24,8 @@ class RandomForestLearner(SklFitter, _FeatureScorerMixin):
'regression': RFRegression}

__returns__ = RandomForestModel

@property
def fitted_parameters(self) -> list[Learner.FittedParameter]:
return [self.FittedParameter("n_estimators", "Number of trees",
int, 1, None)]
12 changes: 8 additions & 4 deletions Orange/regression/pls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from typing import Tuple

import numpy as np
import scipy.stats as ss
import sklearn.cross_decomposition as skl_pls
from sklearn.preprocessing import StandardScaler

from Orange.base import Learner
from Orange.data import Table, Domain, Variable, \
ContinuousVariable, StringVariable
from Orange.data.util import get_unique_names, SharedComputeValue
Expand Down Expand Up @@ -163,11 +162,11 @@ def coefficients_table(self):
return coef_table

@property
def rotations(self) -> Tuple[np.ndarray, np.ndarray]:
def rotations(self) -> tuple[np.ndarray, np.ndarray]:
return self.skl_model.x_rotations_, self.skl_model.y_rotations_

@property
def loadings(self) -> Tuple[np.ndarray, np.ndarray]:
def loadings(self) -> tuple[np.ndarray, np.ndarray]:
return self.skl_model.x_loadings_, self.skl_model.y_loadings_

def residuals_normal_probability(self, data: Table) -> Table:
Expand Down Expand Up @@ -256,6 +255,11 @@ def incompatibility_reason(self, domain):
reason = "Only numeric target variables expected."
return reason

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


if __name__ == '__main__':
import Orange
Expand Down
5 changes: 5 additions & 0 deletions Orange/regression/tests/test_pls.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ def table(rows, attr, variables):


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

def test_allow_y_dim(self):
""" The current PLS version allows only a single Y dimension. """
learner = PLSRegressionLearner(n_components=2)
Expand Down
26 changes: 26 additions & 0 deletions Orange/widgets/evaluate/icons/ParameterFitter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading