99from sklearn .svm import OneClassSVM
1010
1111from Orange .base import SklLearner , SklModel
12- from Orange .data import Table , Domain , DiscreteVariable , ContinuousVariable , \
13- Variable
14- from Orange .data .util import get_unique_names
12+ from Orange .data import Table , Domain , DiscreteVariable , ContinuousVariable
13+ from Orange .data .util import get_unique_names , SharedComputeValue
1514from Orange .preprocess import AdaptiveNormalize
16- from Orange .util import wrap_callback , dummy_callback
15+ from Orange .util import dummy_callback
1716
1817__all__ = ["LocalOutlierFactorLearner" , "IsolationForestLearner" ,
1918 "EllipticEnvelopeLearner" , "OneClassSVMLearner" ]
2019
2120
21+ class _CachedTransform :
22+ # to be used with SharedComputeValue
23+ def __init__ (self , model ):
24+ self .model = model
25+
26+ def __call__ (self , data ):
27+ return self .model .data_to_model_domain (data )
28+
29+
2230class _OutlierModel (SklModel ):
2331 def __init__ (self , skl_model ):
2432 super ().__init__ (skl_model )
25- self ._cached_data = None
2633 self .outlier_var = None
34+ self .cached_transform = _CachedTransform (self )
2735
2836 def predict (self , X : np .ndarray ) -> np .ndarray :
2937 pred = self .skl_model .predict (X )
3038 pred [pred == - 1 ] = 0
3139 return pred [:, None ]
3240
41+ def new_domain (self , data : Table ) -> Domain :
42+ assert self .outlier_var is not None
43+ return Domain (data .domain .attributes , data .domain .class_vars ,
44+ data .domain .metas + (self .outlier_var ,))
45+
3346 def __call__ (self , data : Table , progress_callback : Callable = None ) \
3447 -> Table :
3548 assert isinstance (data , Table )
36- assert self .outlier_var is not None
3749
38- domain = Domain (data .domain .attributes , data .domain .class_vars ,
39- data .domain .metas + (self .outlier_var ,))
50+ domain = self .new_domain (data )
4051 if progress_callback is None :
4152 progress_callback = dummy_callback
42- progress_callback (0 , "Preprocessing..." )
43- self ._cached_data = self .data_to_model_domain (
44- data , wrap_callback (progress_callback , end = 0.1 ))
45- progress_callback (0.1 , "Predicting..." )
46- metas = np .hstack ((data .metas , self .predict (self ._cached_data .X )))
53+ progress_callback (0 , "Predicting..." )
54+ new_table = data .transform (domain )
4755 progress_callback (1 )
48- return Table . from_numpy ( domain , data . X , data . Y , metas )
56+ return new_table
4957
5058
5159class _OutlierLearner (SklLearner ):
@@ -64,27 +72,17 @@ def _fit_model(self, data: Table) -> _OutlierModel:
6472 compute_value = transformer
6573 )
6674
67- transformer .variable = variable
6875 model .outlier_var = variable
6976 return model
7077
7178
72- class _Transformer :
79+ class _Transformer ( SharedComputeValue ) :
7380 def __init__ (self , model : _OutlierModel ):
81+ super ().__init__ (model .cached_transform )
7482 self ._model = model
75- self ._variable = None
76-
77- @property
78- def variable (self ) -> Variable :
79- return self ._variable
8083
81- @variable .setter
82- def variable (self , var : Variable ):
83- self ._variable = var
84-
85- def __call__ (self , data : Table ) -> np .ndarray :
86- assert isinstance (self ._variable , Variable )
87- return self ._model (data ).get_column_view (self ._variable )[0 ]
84+ def compute (self , data : Table , shared_data : Table ) -> np .ndarray :
85+ return self ._model .predict (shared_data .X )[:, 0 ]
8886
8987
9088class OneClassSVMLearner (_OutlierLearner ):
@@ -142,13 +140,16 @@ def mahalanobis(self, observations: np.ndarray) -> np.ndarray:
142140 """
143141 return self .skl_model .mahalanobis (observations )[:, None ]
144142
145- def __call__ (self , data : Table , progress_callback : Callable = None ) \
146- -> Table :
147- pred = super ().__call__ (data , progress_callback )
148- domain = Domain (pred .domain .attributes , pred .domain .class_vars ,
149- pred .domain .metas + (self .mahal_var ,))
150- metas = np .hstack ((pred .metas , self .mahalanobis (self ._cached_data .X )))
151- return Table .from_numpy (domain , pred .X , pred .Y , metas )
143+ def new_domain (self , data : Table ) -> Domain :
144+ assert self .mahal_var is not None
145+ domain = super ().new_domain (data )
146+ return Domain (domain .attributes , domain .class_vars ,
147+ domain .metas + (self .mahal_var ,))
148+
149+
150+ class _TransformerMahalanobis (_Transformer ):
151+ def compute (self , data : Table , shared_data : Table ) -> np .ndarray :
152+ return self ._model .mahalanobis (shared_data .X )[:, 0 ]
152153
153154
154155class EllipticEnvelopeLearner (_OutlierLearner ):
@@ -166,13 +167,12 @@ def _fit_model(self, data: Table) -> EllipticEnvelopeClassifier:
166167 domain = data .domain
167168 model = super ()._fit_model (data .transform (Domain (domain .attributes )))
168169
169- transformer = _Transformer (model )
170+ transformer = _TransformerMahalanobis (model )
170171 names = [v .name for v in domain .variables + domain .metas ]
171172 variable = ContinuousVariable (
172173 get_unique_names (names , "Mahalanobis" ),
173174 compute_value = transformer
174175 )
175176
176- transformer .variable = variable
177177 model .mahal_var = variable
178178 return model
0 commit comments