@@ -89,20 +89,42 @@ def to_array(self):
89
89
90
90
def get_fit (self ) -> None :
91
91
"""Fit a quantile regression model using the interior point method."""
92
- self ._beta_hat = self .fit_qreg (X = self ._X , Y = self ._Y , q = self ._quantile )
92
+ self ._beta_hat = self .fit_qreg_fn (X = self ._X , Y = self ._Y , q = self ._quantile )
93
93
self ._u_hat = self ._Y .flatten () - self ._X @ self ._beta_hat
94
94
self ._hessian = self ._X .T @ self ._X
95
95
self ._bread = np .linalg .inv (self ._hessian )
96
96
97
- def fit_qreg_ip (
97
+ def fit_qreg_fn (self , X : np .ndarray , Y : np .ndarray , q : float ) -> np .ndarray :
98
+ """Fit a quantile regression model using the Frisch-Newton Interior Point Solver."""
99
+ N , _ = X .shape
100
+
101
+ beta_hat , has_converged = frisch_newton_solver (
102
+ A = X .T ,
103
+ b = (1 - q ) * X .T @ np .ones (N ),
104
+ c = - Y ,
105
+ u = np .ones (N ),
106
+ q = q ,
107
+ tol = 1e-6 ,
108
+ max_iter = 50 ,
109
+ backoff = 0.9995 ,
110
+ )
111
+
112
+ if not has_converged :
113
+ warnings .warn (
114
+ "The Frisch-Newton Interior Point solver has not converged after 50 iterations."
115
+ )
116
+
117
+ return - beta_hat .flatten ()
118
+
119
+ def fit_qreg_pfn (
98
120
self ,
99
121
X : np .ndarray ,
100
122
Y : np .ndarray ,
101
123
q : float ,
102
124
rng : np .random .Generator ,
103
125
beta_init : Optional [np .ndarray ] = None ,
104
126
) -> np .ndarray :
105
- """Fit a quantile regression model using the interior point method ."""
127
+ """Fit a quantile regression model using preprocessing and the Frisch-Newton Interior Point Solver ."""
106
128
N , k = self ._X .shape
107
129
has_converged = False
108
130
compute_beta_init = beta_init is None
@@ -118,7 +140,7 @@ def fit_qreg_ip(
118
140
if compute_beta_init :
119
141
# get initial sample
120
142
idx_init = rng .choice (N , size = n_init , replace = False )
121
- beta_hat_init = self .fit_qreg (X [idx_init , :], Y [idx_init ], q = q )
143
+ beta_hat_init = self .fit_qreg_fn (X [idx_init , :], Y [idx_init ], q = q )
122
144
123
145
else :
124
146
beta_hat_init = beta_init
@@ -151,7 +173,7 @@ def fit_qreg_ip(
151
173
152
174
while not has_converged and n_bad_fixups < max_bad_fixups :
153
175
# solve the modified problem
154
- beta_hat = self .fit_qreg (X = X_sub , Y = Y_sub , q = q )
176
+ beta_hat = self .fit_qreg_fn (X = X_sub , Y = Y_sub , q = q )
155
177
r = Y .flatten () - X @ beta_hat
156
178
157
179
# count wrong predictions and get their indices
@@ -173,28 +195,6 @@ def fit_qreg_ip(
173
195
174
196
return beta_hat
175
197
176
- def fit_qreg (self , X : np .ndarray , Y : np .ndarray , q : float ) -> np .ndarray :
177
- """Fit a quantile regression model and return the coefficients."""
178
- N , k = X .shape
179
-
180
- beta_hat , has_converged = frisch_newton_solver (
181
- A = X .T ,
182
- b = (1 - q ) * X .T @ np .ones (N ),
183
- c = - Y ,
184
- u = np .ones (N ),
185
- q = q ,
186
- tol = 1e-6 ,
187
- max_iter = 50 ,
188
- backoff = 0.9995 ,
189
- )
190
-
191
- if not has_converged :
192
- warnings .warn (
193
- "The Frisch-Newton Interior Point solver has not converged after 50 iterations."
194
- )
195
-
196
- return - beta_hat .flatten ()
197
-
198
198
def _vcov_iid (self ) -> np .ndarray :
199
199
raise NotImplementedError (
200
200
"""vcov = 'iid' for quantile regression is not yet implemented. "
@@ -205,11 +205,11 @@ def _vcov_iid(self) -> np.ndarray:
205
205
def _vcov_nid (self ) -> np .ndarray :
206
206
"Compute nonparametric IID (NID) vcov matrix using the Hall-Sheather bandwidth."
207
207
h = get_hall_sheather_bandwidth (q = self ._quantile , N = self ._N )
208
- beta_hat_plus = self .fit_qreg (X = self ._X , Y = self ._Y , q = self ._quantile + h )
209
- # beta_hat_plus = self.fit_qreg_ip (X = self._X, Y = self._Y, q = self._quantile + h, rng = self._rng)
208
+ beta_hat_plus = self .fit_qreg_fn (X = self ._X , Y = self ._Y , q = self ._quantile + h )
209
+ # beta_hat_plus = self.fit_qreg_pfn (X = self._X, Y = self._Y, q = self._quantile + h, rng = self._rng)
210
210
yhat_plus = self ._X @ beta_hat_plus
211
- beta_hat_minus = self .fit_qreg (X = self ._X , Y = self ._Y , q = self ._quantile - h )
212
- # beta_hat_minus = self.fit_qreg_ip (X = self._X, Y = self._Y, q = self._quantile - h, rng = self._rng)
211
+ beta_hat_minus = self .fit_qreg_fn (X = self ._X , Y = self ._Y , q = self ._quantile - h )
212
+ # beta_hat_minus = self.fit_qreg_pfn (X = self._X, Y = self._Y, q = self._quantile - h, rng = self._rng)
213
213
yhat_minus = self ._X @ beta_hat_minus
214
214
215
215
s = (yhat_plus - yhat_minus ) / (2 * h )
@@ -269,7 +269,7 @@ def frisch_newton_solver(
269
269
b = b .flatten ()
270
270
u = u .flatten ()
271
271
272
- x = (1 - 0.5 ) * np .ones (n )
272
+ x = (1 - q ) * np .ones (n )
273
273
s = u - x
274
274
d = c .copy ()
275
275
d_plus = np .maximum (d , 0 )
@@ -285,7 +285,6 @@ def frisch_newton_solver(
285
285
286
286
# 6) Quick sanity checks (optional)
287
287
if True :
288
- # import pdb; pdb.set_trace()
289
288
assert np .all (z > 0 )
290
289
assert np .all (x > 0 )
291
290
assert np .all (s > 0 )
@@ -331,8 +330,8 @@ def step_length(a: tuple, b: tuple, backoff: float = 0.9995):
331
330
dw_aff = - w - (w / s ) * ds_aff
332
331
333
332
# Step lengths (eq. (9))
334
- alpha_p_aff = step_length (a = (x , dx_aff ), b = (s , ds_aff ))
335
- alpha_d_aff = step_length (a = (z , dz_aff ), b = (w , dw_aff ))
333
+ alpha_p_aff = step_length (a = (x , dx_aff ), b = (s , ds_aff ), backoff = backoff )
334
+ alpha_d_aff = step_length (a = (z , dz_aff ), b = (w , dw_aff ), backoff = backoff )
336
335
337
336
# 6) Compute mu_new and centering sigma (eq (10))
338
337
x_pred = x + alpha_p_aff * dx_aff
@@ -359,8 +358,8 @@ def step_length(a: tuple, b: tuple, backoff: float = 0.9995):
359
358
dw_cor = - (w / s ) * ds_cor + (mu_targ - ds_aff * dw_aff ) / s
360
359
361
360
# 9) Final step lengths (corrector) — eq (12)
362
- alpha_p_cor = step_length (a = (x , dx_cor ), b = (s , ds_cor ))
363
- alpha_d_cor = step_length (a = (z , dz_cor ), b = (w , dw_cor ))
361
+ alpha_p_cor = step_length (a = (x , dx_cor ), b = (s , ds_cor ), backoff = backoff )
362
+ alpha_d_cor = step_length (a = (z , dz_cor ), b = (w , dw_cor ), backoff = backoff )
364
363
365
364
# 10) Update all variables / corrector step
366
365
# Update
0 commit comments