source: orange/orange/Orange/regression/lasso.py @ 9545:b8492dd6f2cd

Revision 9545:b8492dd6f2cd, 13.7 KB checked in by lanz <lan.zagar@…>, 2 years ago (diff)

Fixed 2 documentation warnings.

Line 
1"""\
2============================
3Lasso regression (``lasso``)
4============================
5
6.. index:: regression
7
8.. _`Lasso regression. Regression shrinkage and selection via the lasso`:
9    http://www-stat.stanford.edu/~tibs/lasso/lasso.pdf
10
11
12Example ::
13
14    >>> from Orange.regression import lasso
15    >>> table = Orange.data.Table("housing")
16    >>> c = lasso.LassoRegressionLearner(table)
17    >>> print c
18   
19      Variable  Coeff Est  Std Error          p
20     Intercept     22.533
21          CRIM     -0.044      0.030      0.510     
22            ZN      0.013      0.010      0.660     
23         INDUS     -0.003      0.023      0.980     
24          CHAS      2.318      1.304      0.200     
25           NOX     -7.530      2.803      0.370     
26            RM      4.231      0.819      0.000   ***
27           DIS     -0.710      0.130      0.070     .
28           RAD      0.074      0.029      0.510     
29           TAX     -0.004      0.002      0.560     
30       PTRATIO     -0.821      0.095      0.000   ***
31             B      0.007      0.002      0.170     
32         LSTAT     -0.503      0.085      0.000   ***
33    Signif. codes:  0 *** 0.001 ** 0.01 * 0.05 . 0.1 empty 1
34
35
36    For 1 variable the regression coefficient equals 0:
37    AGE
38       
39    >>>
40
41
42.. autoclass:: LassoRegressionLearner
43    :members:
44
45.. autoclass:: LassoRegression
46    :members:
47
48Utility functions
49-----------------
50
51.. autofunction:: center
52
53.. autofunction:: get_bootstrap_sample
54
55.. autofunction:: permute_responses
56
57"""
58
59import Orange
60import numpy
61
62from Orange.regression import base
63
64from Orange.misc import deprecated_members, deprecated_keywords
65
66def center(X):
67    """Centers the data, i.e. subtracts the column means.
68    Returns the centered data and the mean.
69
70    :param X: the data arry
71    :type table: :class:`numpy.array`
72    """
73    mu = X.mean(axis=0)
74    return X - mu, mu
75
76def standardize(X):
77    """Standardizes the data, i.e. subtracts the column means and divide by
78    standard deviation.
79    Returns the centered data, the mean, and deviations.
80
81    :param X: the data arry
82    :type table: :class:`numpy.array`
83    """
84    mu = numpy.mean(X, axis=0)
85    std = numpy.std(X, axis=0)
86    return (X - mu) / std, mu, std
87
88def get_bootstrap_sample(table):
89    """Generates boostrap sample from an Orange Example Table
90    and stores it in a new :class:`Orange.data.Table` object
91
92    :param table: the original data sample
93    :type table: :class:`Orange.data.Table`
94    """
95    n = len(table)
96    bootTable = Orange.data.Table(table.domain)
97    for i in range(n):
98        id = numpy.random.randint(0,n)
99        bootTable.append(table[id])
100    return bootTable
101
102def permute_responses(table):
103    """ Permutes values of the class (response) variable.
104    The independence between independent variables and the response
105    is obtained but the distribution of the response variable is kept.
106
107    :param table: the original data sample
108    :type table: :class:`Orange.data.Table`
109    """
110    n = len(table)
111    perm = numpy.random.permutation(n)
112    permTable = Orange.data.Table(table.domain, table)
113    for i, ins in enumerate(table):
114        permTable[i].set_class(table[perm[i]].get_class())
115    return permTable
116
117class LassoRegressionLearner(base.BaseRegressionLearner):
118    """Fits the lasso regression model, i.e. learns the regression parameters
119    The class is derived from
120    :class:`Orange.regression.base.BaseRegressionLearner`
121    which is used for preprocessing the data (continuization and imputation)
122    before fitting the regression parameters
123
124    """
125
126    def __init__(self, name='lasso regression', t=1, s=None, tol=0.001, \
127                 n_boot=100, n_perm=100, imputer=None, continuizer=None):
128        """
129        :param name: name of the linear model, default 'lasso regression'
130        :type name: string
131       
132        :param t: tuning parameter, upper bound for the L1-norm of the
133            regression coefficients
134        :type t: float
135       
136        :param s: An alternative way to specify the tuning parameter ``t``.
137            Here ``t`` is taken to be t = s * sum(abs(B)) where B are the
138            coefficients of an ordinary least square linear fit. ``t`` parameter is ignored if ``s`` is specified (by default it
139            is None).
140        :type s: float
141       
142        :param tol: tolerance parameter, regression coefficients
143            (absoulute value) under tol are set to 0,
144            default=0.001
145        :type tol: float
146       
147        :param n_boot: number of bootstrap samples used for non-parametric
148            estimation of standard errors
149        :type n_boot: int
150       
151        :param n_perm: number of permuations used for non-parametric
152            estimation of p-values
153        :type n_perm: int
154       
155        """
156
157        self.name = name
158        self.t = t
159        self.s = s
160        self.tol = tol
161        self.n_boot = n_boot
162        self.n_perm = n_perm
163        self.set_imputer(imputer=imputer)
164        self.set_continuizer(continuizer=continuizer)
165       
166       
167    def __call__(self, table, weight=None):
168        """
169        :param table: data instances.
170        :type table: :class:`Orange.data.Table`
171        :param weight: the weights for instances. Default: None, i.e.
172            all data instances are eqaully important in fitting
173            the regression parameters
174        :type weight: None or list of Orange.data.variable.Continuous
175            which stores weights for instances
176       
177        """ 
178        # dicrete values are continuized       
179        table = self.continuize_table(table)
180        # missing values are imputed
181        table = self.impute_table(table)
182
183        domain = table.domain
184        X, y, w = table.to_numpy()
185        n, m = numpy.shape(X)
186       
187        X, mu_x, sigma_x = standardize(X)
188        y, coef0 = center(y)
189       
190        t = self.t
191       
192        if self.s is not None:
193            beta_full, rss, _, _ = numpy.linalg.lstsq(X, y)
194            t = self.s * numpy.sum(numpy.abs(beta_full))
195            print "t =", t
196           
197        import scipy.optimize
198           
199        # objective function to be minimized
200        objective = lambda beta: numpy.linalg.norm(y - numpy.dot(X, beta))
201        # initial guess for the regression parameters
202        beta_init = numpy.random.random(m)
203        # constraints for the regression coefficients
204        cnstr = lambda beta: t - numpy.sum(numpy.abs(beta))
205        # optimal solution
206        coefficients = scipy.optimize.fmin_cobyla(objective, beta_init,\
207                                                       cnstr, disp=0)
208
209        # set small coefficients to 0
210        def set_2_0(c): return c if abs(c) > self.tol else 0
211        coefficients = numpy.array(map(set_2_0, coefficients))
212        coefficients /= sigma_x
213       
214        # bootstrap estimator of standard error of the coefficient estimators
215        # assumption: fixed t
216        if self.n_boot > 0:
217            coeff_b = [] # bootstrapped coefficients
218            for i in range(self.n_boot):
219                tmp_table = get_bootstrap_sample(table)
220                l = LassoRegressionLearner(t=t, n_boot=0, n_perm=0)
221                c = l(tmp_table)
222                coeff_b.append(c.coefficients)
223            std_errors_fixed_t = numpy.std(coeff_b, axis=0)
224        else:
225            std_errors_fixed_t = [float("nan")] * m
226
227        # permutation test to obtain the significance of the regression
228        #coefficients
229        if self.n_perm > 0:
230            coeff_p = []
231            for i in range(self.n_perm):
232                tmp_table = permute_responses(table)
233                l = LassoRegressionLearner(t=t, n_boot=0, n_perm=0)
234                c = l(tmp_table)
235                coeff_p.append(c.coefficients)
236            p_vals = \
237                   numpy.sum(abs(numpy.array(coeff_p))>\
238                             abs(numpy.array(coefficients)), \
239                             axis=0)/float(self.n_perm)
240        else:
241            p_vals = [float("nan")] * m
242
243        # dictionary of regression coefficients with standard errors
244        # and p-values
245        dict_model = {}
246        for i, var in enumerate(domain.attributes):
247            dict_model[var.name] = (coefficients[i], std_errors_fixed_t[i], p_vals[i])           
248       
249        return LassoRegression(domain=domain, class_var=domain.class_var,
250                               coef0=coef0, coefficients=coefficients,
251                               std_errors_fixed_t=std_errors_fixed_t,
252                               p_vals=p_vals,
253                               dict_model= dict_model,
254                               mu_x=mu_x)
255
256deprecated_members({"nBoot": "n_boot",
257                    "nPerm": "n_perm"}, 
258                   wrap_methods=["__init__"],
259                   in_place=True)(LassoRegressionLearner)
260
261class LassoRegression(Orange.classification.Classifier):
262    """Lasso regression predicts value of the response variable
263    based on the values of independent variables.
264
265    .. attribute:: coef0
266
267        intercept (sample mean of the response variable)   
268
269    .. attribute:: coefficients
270
271        list of regression coefficients.
272
273    .. attribute:: std_errors_fixed_t
274
275        list of standard errors of the coefficient estimator for the fixed
276        tuning parameter t. The standard errors are estimated using
277        bootstrapping method.
278
279    .. attribute:: p_vals
280
281        list of p-values for the null hypothesis that the regression
282        coefficients equal 0 based on non-parametric permutation test
283
284    .. attribute:: dict_model
285
286        dictionary of statistical properties of the model.
287        Keys - names of the independent variables
288        Values - tuples (coefficient, standard error, p-value)
289
290    .. attribute:: mu_x
291
292        the sample mean of the all independent variables   
293
294    """ 
295    def __init__(self, domain=None, class_var=None, coef0=None,
296                 coefficients=None, std_errors_fixed_t=None, p_vals=None,
297                 dict_model=None, mu_x=None):
298        self.domain = domain
299        self.class_var = class_var
300        self.coef0 = coef0
301        self.coefficients = coefficients
302        self.std_errors_fixed_t = std_errors_fixed_t
303        self.p_vals = p_vals
304        self.dict_model = dict_model
305        self.mu_x = mu_x
306
307    @deprecated_keywords({"resultType": "result_type"})
308    def __call__(self, instance, result_type=Orange.core.GetValue):
309        """
310        :param instance: data instance for which the value of the response
311            variable will be predicted
312        :type instance:
313        """ 
314        ins = Orange.data.Instance(self.domain, instance)
315        if "?" in ins: # missing value -> corresponding coefficient omitted
316            def miss_2_0(x): return x if x != "?" else 0
317            ins = map(miss_2_0, ins)
318            ins = numpy.array(ins)[:-1] - self.mu_x
319        else:
320            ins = numpy.array(ins.native())[:-1] - self.mu_x
321
322        y_hat = numpy.dot(self.coefficients, ins) + self.coef0
323        y_hat = self.class_var(y_hat)
324        dist = Orange.statistics.distribution.Continuous(self.class_var)
325        dist[y_hat] = 1.0
326        if result_type == Orange.core.GetValue:
327            return y_hat
328        if result_type == Orange.core.GetProbabilities:
329            return dist
330        else:
331            return (y_hat, dist)
332       
333    @deprecated_keywords({"skipZero": "skip_zero"})
334    def to_string(self, skip_zero=True):
335        """Pretty-prints Lasso regression model,
336        i.e. estimated regression coefficients with standard errors
337        and significances. Standard errors are obtained using bootstrapping
338        method and significances by the permuation test
339
340        :param skip_zero: if True variables with estimated coefficient equal to 0
341            are omitted
342        :type skip_zero: boolean
343        """
344       
345        from string import join
346        labels = ('Variable', 'Coeff Est', 'Std Error', 'p')
347        lines = [join(['%10s' % l for l in labels], ' ')]
348
349        fmt = "%10s " + join(["%10.3f"]*3, " ") + " %5s"
350        fmt1 = "%10s %10.3f"
351
352        def get_star(p):
353            if p < 0.001: return  "*"*3
354            elif p < 0.01: return "*"*2
355            elif p < 0.05: return "*"
356            elif p < 0.1: return  "."
357            else: return " "
358
359        stars =  get_star(self.p_vals[0])
360        lines.append(fmt1 % ('Intercept', self.coef0))
361        skipped = []
362        for i in range(len(self.domain.attributes)):
363            if self.coefficients[i] == 0. and skip_zero:
364                skipped.append(self.domain.attributes[i].name)
365                continue           
366            stars = get_star(self.p_vals[i])
367            lines.append(fmt % (self.domain.attributes[i].name, 
368                         self.coefficients[i], self.std_errors_fixed_t[i], 
369                         self.p_vals[i], stars))
370        lines.append("Signif. codes:  0 *** 0.001 ** 0.01 * 0.05 . 0.1 empty 1")
371        lines.append("\n")
372        if skip_zero:
373            k = len(skipped)
374            if k == 0:
375                lines.append("All variables have non-zero regression coefficients. ")
376            else:
377                suff = "s" if k > 1 else ""
378                lines.append("For %d variable%s the regression coefficient equals 0: " \
379                      % (k, suff))
380                for var in skipped:
381                    lines.append(var)
382        return "\n".join(lines)
383
384    def __str__(self):
385        return self.to_string(skip_zero=True)
386
387deprecated_members({"muX": "mu_x",
388                    "stdErrorsFixedT": "std_errors_fixed_t",
389                    "pVals": "p_vals",
390                    "dictModel": "dict_model"},
391                   wrap_methods=["__init__"],
392                   in_place=True)(LassoRegression)
393
394if __name__ == "__main__":
395
396    import Orange
397   
398    table = Orange.data.Table("housing.tab")       
399
400    c = LassoRegressionLearner(table, t=len(table.domain))
401    print c
Note: See TracBrowser for help on using the repository browser.