7
7
8
8
import sys
9
9
import warnings
10
+ import numbers
10
11
from abc import ABCMeta , abstractmethod
11
12
12
13
import numpy as np
20
21
from ..utils .validation import check_random_state
21
22
from ..model_selection import check_cv
22
23
from ..utils .extmath import safe_sparse_dot
23
- from ..utils .fixes import _joblib_parallel_args
24
- from ..utils .validation import check_is_fitted
24
+ from ..utils .fixes import _astype_copy_false , _joblib_parallel_args
25
+ from ..utils .validation import check_is_fitted , _check_sample_weight
25
26
from ..utils .validation import column_or_1d
26
27
27
28
from . import _cd_fast as cd_fast
28
29
29
30
31
+ def _set_order (X , y , order = 'C' ):
32
+ """Change the order of X and y if necessary.
33
+
34
+ Parameters
35
+ ----------
36
+ X : {array-like, sparse matrix} of shape (n_samples, n_features)
37
+ Training data.
38
+
39
+ y : ndarray of shape (n_samples,)
40
+ Target values.
41
+
42
+ order : {None, 'C', 'F'}
43
+ If 'C', dense arrays are returned as C-ordered, sparse matrices in csr
44
+ format. If 'F', dense arrays are return as F-ordered, sparse matrices
45
+ in csc format.
46
+
47
+ Returns
48
+ -------
49
+ X : {array-like, sparse matrix} of shape (n_samples, n_features)
50
+ Training data with guaranteed order.
51
+
52
+ y : ndarray of shape (n_samples,)
53
+ Target values with guaranteed order.
54
+ """
55
+ if order not in [None , 'C' , 'F' ]:
56
+ raise ValueError ("Unknown value for order. Got {} instead of "
57
+ "None, 'C' or 'F'." .format (order ))
58
+ sparse_X = sparse .issparse (X )
59
+ sparse_y = sparse .issparse (y )
60
+ if order is not None :
61
+ sparse_format = "csc" if order == "F" else "csr"
62
+ if sparse_X :
63
+ # As of scipy 1.1.0, new argument copy=False by default.
64
+ # This is what we want.
65
+ X = X .asformat (sparse_format , ** _astype_copy_false (X ))
66
+ else :
67
+ X = np .asarray (X , order = order )
68
+ if sparse_y :
69
+ y = y .asformat (sparse_format )
70
+ else :
71
+ y = np .asarray (y , order = order )
72
+ return X , y
73
+
74
+
30
75
###############################################################################
31
76
# Paths functions
32
77
@@ -661,7 +706,7 @@ def __init__(self, alpha=1.0, l1_ratio=0.5, fit_intercept=True,
661
706
self .random_state = random_state
662
707
self .selection = selection
663
708
664
- def fit (self , X , y , check_input = True ):
709
+ def fit (self , X , y , sample_weight = None , check_input = True ):
665
710
"""Fit model with coordinate descent.
666
711
667
712
Parameters
@@ -673,6 +718,9 @@ def fit(self, X, y, check_input=True):
673
718
(n_samples, n_targets)
674
719
Target. Will be cast to X's dtype if necessary
675
720
721
+ sample_weight : float or array-like of shape (n_samples,), default=None
722
+ Sample weight.
723
+
676
724
check_input : bool, default=True
677
725
Allow to bypass several input checking.
678
726
Don't use this parameter unless you know what you do.
@@ -709,18 +757,49 @@ def fit(self, X, y, check_input=True):
709
757
y = check_array (y , order = 'F' , copy = False , dtype = X .dtype .type ,
710
758
ensure_2d = False )
711
759
712
- # Ensure copying happens only once, don't do it again if done above
760
+ n_samples , n_features = X .shape
761
+ alpha = self .alpha
762
+
763
+ if isinstance (sample_weight , numbers .Number ):
764
+ sample_weight = None
765
+ if sample_weight is not None :
766
+ if check_input :
767
+ if sparse .issparse (X ):
768
+ raise ValueError ("Sample weights do not (yet) support "
769
+ "sparse matrices." )
770
+ sample_weight = _check_sample_weight (sample_weight , X ,
771
+ dtype = X .dtype )
772
+ # simplify things by rescaling sw to sum up to n_samples
773
+ # => np.average(x, weights=sw) = np.mean(sw * x)
774
+ sample_weight *= (n_samples / np .sum (sample_weight ))
775
+ # Objective function is:
776
+ # 1/2 * np.average(squared error, weights=sw) + alpha * penalty
777
+ # but coordinate descent minimizes:
778
+ # 1/2 * sum(squared error) + alpha * penalty
779
+ # enet_path therefore sets alpha = n_samples * alpha
780
+ # With sw, enet_path should set alpha = sum(sw) * alpha
781
+ # Therefore, we rescale alpha = sum(sw) / n_samples * alpha
782
+ # Note: As we rescaled sample_weights to sum up to n_samples,
783
+ # we don't need this
784
+ # alpha *= np.sum(sample_weight) / n_samples
785
+
786
+ # Ensure copying happens only once, don't do it again if done above.
787
+ # X and y will be rescaled if sample_weight is not None, order='F'
788
+ # ensures that the returned X and y are still F-contiguous.
713
789
should_copy = self .copy_X and not X_copied
F438
714
790
X , y , X_offset , y_offset , X_scale , precompute , Xy = \
715
791
_pre_fit (X , y , None , self .precompute , self .normalize ,
716
792
self .fit_intercept , copy = should_copy ,
717
- check_input = check_input )
793
+ check_input = check_input , sample_weight = sample_weight )
794
+ # coordinate descent needs F-ordered arrays and _pre_fit might have
795
+ # called _rescale_data
796
+ if check_input or sample_weight is not None :
797
+ X , y = _set_order (X , y , order = 'F' )
718
798
if y .ndim == 1 :
719
799
y = y [:, np .newaxis ]
720
800
if Xy is not None and Xy .ndim == 1 :
721
801
Xy = Xy [:, np .newaxis ]
722
802
723
- n_samples , n_features = X .shape
724
803
n_targets = y .shape [1 ]
725
804
726
805
if self .selection not in ['cyclic' , 'random' ]:
@@ -745,7 +824,7 @@ def fit(self, X, y, check_input=True):
745
824
_ , this_coef , this_dual_gap , this_iter = \
746
825
self .path (X , y [:, k ],
747
826
l1_ratio = self .l1_ratio , eps = None ,
748
- n_alphas = None , alphas = [self . alpha ],
827
+ n_alphas = None , alphas = [alpha ],
749
828
precompute = precompute , Xy = this_Xy ,
750
829
fit_intercept = False , normalize = False , copy_X = True ,
751
830
verbose = False , tol = self .tol , positive = self .positive ,
@@ -1397,6 +1476,7 @@ def __init__(self, eps=1e-3, n_alphas=100, alphas=None, fit_intercept=True,
1397
1476
def _more_tags (self ):
1398
1477
return {'multioutput' : False }
1399
1478
1479
+
1400
1480
class ElasticNetCV (RegressorMixin , LinearModelCV ):
1401
1481
"""Elastic Net model with iterative fitting along a regularization path.
1402
1482
0 commit comments