Skip to content

Commit 2fb76b3

Browse files
Fix small sample correction handling for HAC (#1045)
* new clean branch for the draft PR * clarify origin of default lags * multiple smaller fixes * commit to allow merge * updated NW Meat function to match fixest implementation * Newey-West complete. Tests and documentation pending * pass function attributes along, force _nw_meat to work on arrays only (else numba does not compile) * add basuc unit tests for HAC * fix test bug? * one more fix * fix more bugs * fix tests * pacify mypy' * test for errors * add test for update * update type hints for lags * rename arg from lags to lag for compatibility with fixest * require lag, time_id for HAC * panel HAC first PR * added balance function * allow for non-balances panel and non-consecutive data * _dk_meat added. documentation and tests are pending * fix indentation error & njit & linter & mypy * prepare tests for non-consecutive implementation * good speed ups of time series hac * fixed everything and added fixef_maxiter everywhere * more numba for DK * code reorg + check for unique time and panel * more adjustment to new syntax * update get_panel_idx * tests for fepois, glm * ar 1 errors * add unrelated test error * fix test bugs * compure panel hac on scores, fix bugs for glms, support glms * update readme * update tests * fix linter * some progress ssc * pre commit * fix HAC ssc * fix * delete print statements --------- Co-authored-by: Daman Dhaliwal <[email protected]>
1 parent d1bd01a commit 2fb76b3

File tree

4 files changed

+18
-41
lines changed

4 files changed

+18
-41
lines changed

pyfixest/estimation/feols_.py

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ def __init__(
327327
self._demean_func = impl["demean"]
328328
self._find_collinear_variables_func = impl["collinear"]
329329
self._crv1_meat_func = impl["crv1_meat"]
330-
self._cound_nested_fixef_func = impl["nonnested"]
330+
self._count_nested_fixef_func = impl["nonnested"]
331331

332332
# set in get_fit()
333333
self._tZX = np.array([])
@@ -690,32 +690,18 @@ def vcov(
690690
self._vcov = self._ssc * self._vcov_hetero()
691691

692692
elif self._vcov_type == "HAC":
693-
if self._ssc_dict["k_adj"]:
694-
self._ssc_dict["k_adj"] = False
695-
warnings.warn(
696-
"k_adj was set to False for HAC inference because that's currently the only supported option."
697-
)
698-
if self._ssc_dict["G_adj"]:
699-
self._ssc_dict["G_adj"] = False
700-
warnings.warn(
701-
"G_adj was set to False for HAC inference because that's currently the only supported option."
702-
)
703-
if self._ssc_dict["k_fixef"] == "nonnested":
704-
self._ssc_dict["k_fixef"] = "none"
705-
warnings.warn(
706-
"k_fixef was set to none for HAC inference because nonnested is currently not supported."
707-
)
708-
709693
ssc_kwargs_hac = {
710-
"k_fe_nested": 0,
711-
"n_fe_fully_nested": 0,
694+
"k_fe_nested": 0, # nesting ignored / irrelevant for HAC SEs
695+
"n_fe_fully_nested": 0, # nesting ignored / irrelevant for HAC SEs
712696
"vcov_sign": 1,
713697
"vcov_type": "HAC",
714-
"G": self._N,
698+
"G": np.unique(self._data[self._time_id]).shape[
699+
0
700+
], # number of unique time periods T used
715701
}
716702

717703
all_kwargs = {**ssc_kwargs, **ssc_kwargs_hac}
718-
self._ssc, self._dof_k, self._df_t = get_ssc(**all_kwargs)
704+
self._ssc, self._df_k, self._df_t = get_ssc(**all_kwargs)
719705

720706
self._vcov = self._ssc * self._vcov_hac()
721707

@@ -770,7 +756,7 @@ def vcov(
770756
k_fe_nested = 0
771757
n_fe_fully_nested = 0
772758
if self._has_fixef and self._ssc_dict["k_fixef"] == "nonnested":
773-
k_fe_nested_flag, n_fe_fully_nested = self._cound_nested_fixef_func(
759+
k_fe_nested_flag, n_fe_fully_nested = self._count_nested_fixef_func(
774760
all_fixef_array=np.array(
775761
self._fixef.replace("^", "_").split("+"), dtype=str
776762
),

pyfixest/utils/utils.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def get_ssc(
218218
adj_value = (N - 1) / (N - df_k) if vcov_type != "hetero" else N / (N - df_k)
219219

220220
# G_adj applied with G = N for hetero but not for iid
221-
if vcov_type in ["CRV"] and G_adj:
221+
if vcov_type in ["CRV", "HAC"] and G_adj:
222222
if G_df == "conventional":
223223
G_adj_value = G / (G - 1)
224224
elif G_df == "min":
@@ -227,8 +227,7 @@ def get_ssc(
227227
else:
228228
raise ValueError("G_df is neither conventional nor min.")
229229

230-
df_t = N - df_k if vcov_type in ["iid", "hetero"] else G - 1
231-
230+
df_t = N - df_k if vcov_type in ["iid", "hetero", "HAC-TS"] else G - 1
232231
return np.array([adj_value * G_adj_value * vcov_sign]), df_k, df_t
233232

234233

tests/test_errors.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,13 +1069,3 @@ def test_errors_hac():
10691069
vcov=vcov,
10701070
vcov_kwargs={"time_id": "time3", "panel_id": "panel", "lag": 5},
10711071
)
1072-
1073-
1074-
def test_errors_hac_inference():
1075-
data = pf.get_data()
1076-
data["Y"] = np.where(data["Y"] > 0, 1, 0)
1077-
with pytest.raises(
1078-
NotImplementedError,
1079-
match=r"HAC inference is not supported for this model type\.",
1080-
):
1081-
pf.quantreg("Y ~ X1", data=data, vcov="NW")

tests/test_hac_vs_fixest.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ def _get_r_panel_kwargs(time_id, panel_id, lag, inference):
271271
[None, "weights"],
272272
)
273273
@pytest.mark.parametrize("fml", ols_fmls)
274+
@pytest.mark.parametrize("k_adj", [True, False])
275+
@pytest.mark.parametrize("G_adj", [True, False])
276+
@pytest.mark.parametrize("k_fixef", ["none", "nonnested", "full"])
274277
def test_single_fit_feols_hac_panel(
275278
data_panel,
276279
data_time,
@@ -279,11 +282,10 @@ def test_single_fit_feols_hac_panel(
279282
weights,
280283
fml,
281284
balanced,
285+
k_adj,
286+
G_adj,
287+
k_fixef,
282288
):
283-
k_adj = False
284-
G_adj = False
285-
ssc_ = ssc(k_adj=k_adj, G_adj=G_adj)
286-
287289
lag = vcov_kwargs.get("lag", None)
288290
time_id = vcov_kwargs.get("time_id", None)
289291
panel_id = vcov_kwargs.get("panel_id", None)
@@ -310,9 +312,9 @@ def test_single_fit_feols_hac_panel(
310312
if inference == "NW"
311313
else fixest.vcov_DK(**r_panel_kwars),
312314
data=data,
313-
ssc=fixest.ssc(k_adj, "nested", False, G_adj, "min", "min"),
314315
**({"weights": ro.Formula(f"~{weights}")} if weights is not None else {}),
315316
panel_time_step=1,
317+
ssc=fixest.ssc(k_adj, k_fixef, False, G_adj, "min", "min"),
316318
)
317319

318320
mod = pf.feols(
@@ -321,7 +323,7 @@ def test_single_fit_feols_hac_panel(
321323
vcov=inference,
322324
vcov_kwargs=vcov_kwargs,
323325
weights=weights,
324-
ssc=ssc_,
326+
ssc=pf.ssc(k_adj=k_adj, k_fixef=k_fixef, G_adj=G_adj),
325327
)
326328

327329
# r_fixest to global r env, needed for

0 commit comments

Comments
 (0)