11# License: BSD 3 clause
22
3- # -*- coding: utf8 -*-
4-
53from tick .optim .prox .base import Prox
64import numpy as np
75
8- from tick .optim .prox .build .prox import ProxSortedL1 as _ProxSortedL1
9- from tick .optim .prox .build .prox import WeightsType_bh , \
10- WeightsType_oscar
11-
12-
13- # TODO: put also the OSCAR weights
14- # TODO: we should be able to put any weights we want...
6+ from tick .optim .prox .build .prox import ProxOscar as _ProxOscar
157
168
17- class ProxSortedL1 (Prox ):
18- """Proximal operator of sorted L1 penalization
9+ class ProxOscar (Prox ):
10+ """Proximal operator of the OSCAR penalization.
11+ This penalization combines L1 penalization with a clustering penalization,
12+ that induces exact equality of weights corresponding to correlated features,
13+ so that clusters can be represented by a single coefficient.
14+ This penalization is therefore particularly relevant in high-dimensional
15+ problems with strong features correlation.
1916
2017 Parameters
2118 ----------
2219 strength : `float`
2320 Level of penalization
2421
25- fdr : `float`, default=0.6
26- Desired False Discovery Rate for detection of non-zeros in
27- the coefficients
28-
29- weights_type : "bh" | "oscar", default="bh"
30-
31- * If "bh", we use Benjamini-Hochberg weights, under a Gaussian
32- error assumption, and expect a FDR control
33- * If "oscar", there is no FDR control, and we get the OSCAR
34- penalization, see notes below for references
22+ ratio : `float`
23+ The Oscar ratio parameter, with ratio >= 0. For ratio = 0 this is L1
24+ regularization, while a large ratio provides only the clustering effect.
3525
3626 range : `tuple` of two `int`, default=`None`
3727 Range on which the prox is applied. If `None` then the prox is
3828 applied on the whole vector
3929
40- Attributes
41- ----------
42- weights : `np.array`, shape=(n_coeffs,)
43- The weights used in the penalization. They are automatically
44- setted, depending on the ``weights_type`` and ``fdr``
45- parameters.
30+ positive : `bool`, default=`False`
31+ If True, apply an extra projection onto the set of vectors with
32+ non-negative entries
4633
4734 Notes
4835 -----
49- Uses the stack-based algorithm for FastProxL1 from
36+ This penalization was introduced in
37+ * Simultaneous regression shrinkage, variable selection and clustering of
38+ predictors with OSCAR, by Bondell H.D. and Reich B.J., Biometrics. 2008
39+
40+ It uses the stack-based algorithm for FastProxL1 from
5041
5142 * SLOPE--Adaptive Variable Selection via Convex Optimization, by
5243 Bogdan, M. and Berg, E. van den and Sabatti, C. and Su, W. and Candes, E. J.
@@ -58,59 +49,28 @@ class ProxSortedL1(Prox):
5849 "writable" : True ,
5950 "cpp_setter" : "set_strength"
6051 },
61- "fdr " : {
52+ "ratio " : {
6253 "writable" : True ,
63- "cpp_setter" : "set_fdr"
64- },
65- "_weights_type" : {
66- "writable" : False ,
67- "cpp_setter" : "set_weights_type"
54+ "cpp_setter" : "set_ratio"
6855 },
6956 "positive" : {
7057 "writable" : True ,
7158 "cpp_setter" : "set_positive"
72- },
73- "weights" : {
74- "writable" : False ,
7559 }
7660 }
7761
78- def __init__ (self , strength : float , fdr : float = 0.6 ,
79- weights_type : str = "bh" , range : tuple = None ,
62+ def __init__ (self , strength : float , ratio : float , range : tuple = None ,
8063 positive : bool = False ):
8164 Prox .__init__ (self , range )
8265 self .strength = strength
83- self .fdr = fdr
84- self .weights_type = weights_type
66+ self .ratio = ratio
8567 self .positive = positive
8668 self .weights = None
8769 if range is None :
88- self ._prox = _ProxSortedL1 (self .strength , self .fdr ,
89- self ._weights_type ,
90- self .positive )
70+ self ._prox = _ProxOscar (self .strength , self .positive )
9171 else :
92- self ._prox = _ProxSortedL1 (self .strength , self .fdr ,
93- self ._weights_type ,
94- self .range [0 ], self .range [1 ],
95- self .positive )
96-
97- @property
98- def weights_type (self ):
99- if self ._weights_type == WeightsType_bh :
100- return "bh"
101- elif self ._weights_type == WeightsType_oscar :
102- return "oscar"
103-
104- @weights_type .setter
105- def weights_type (self , val ):
106- if val == "bh" :
107- self ._set ("_weights_type" , WeightsType_bh )
108- elif val == "oscar" :
109- self ._set ("_weights_type" , WeightsType_oscar )
110- raise NotImplementedError ("``oscar`` weights." )
111- else :
112- raise ValueError ("``weights_type`` must be either 'bh' "
113- "or 'oscar'" )
72+ self ._prox = _ProxOscar (self .strength , self .range [0 ], self .range [1 ],
73+ self .positive )
11474
11575 def _call (self , coeffs : np .ndarray , t : float , out : np .ndarray ):
11676 self ._prox .call (coeffs , t , out )
@@ -130,8 +90,3 @@ def value(self, coeffs: np.ndarray) -> float:
13090 Value of the penalization at ``coeffs``
13191 """
13292 return self ._prox .value (coeffs )
133-
134- def _as_dict (self ):
135- dd = Prox ._as_dict (self )
136- del dd ["weights" ]
137- return dd
0 commit comments