Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d4612db
new clean branch for the draft PR
damandhaliwal Aug 27, 2025
7a9a31d
clarify origin of default lags
s3alfisc Aug 27, 2025
358b47b
multiple smaller fixes
s3alfisc Aug 28, 2025
3928dc5
commit to allow merge
damandhaliwal Aug 30, 2025
4230f04
Merge branch 'issue675HAC' of github.com:damandhaliwal/pyfixest into …
damandhaliwal Aug 30, 2025
fd20d59
updated NW Meat function to match fixest implementation
damandhaliwal Aug 30, 2025
2b753d9
Newey-West complete. Tests and documentation pending
damandhaliwal Aug 30, 2025
ecb3b61
pass function attributes along, force _nw_meat to work on arrays only…
s3alfisc Aug 31, 2025
9d4d704
add basuc unit tests for HAC
s3alfisc Aug 31, 2025
3fb2e29
fix test bug?
s3alfisc Aug 31, 2025
24b5b8e
one more fix
s3alfisc Aug 31, 2025
4632682
fix more bugs
s3alfisc Aug 31, 2025
2ae3cce
fix tests
s3alfisc Aug 31, 2025
ed9c52b
pacify mypy'
s3alfisc Aug 31, 2025
52052d1
test for errors
s3alfisc Aug 31, 2025
8b4e7d7
add test for update
s3alfisc Aug 31, 2025
d66b5d7
update type hints for lags
s3alfisc Aug 31, 2025
cc98167
rename arg from lags to lag for compatibility with fixest
s3alfisc Sep 1, 2025
43b8905
require lag, time_id for HAC
s3alfisc Sep 5, 2025
b3b37f3
panel HAC first PR
s3alfisc Sep 6, 2025
e402266
added balance function
damandhaliwal Sep 6, 2025
89622ad
Merge branch 'issue675HAC' of github.com:damandhaliwal/pyfixest into …
damandhaliwal Sep 6, 2025
7d312a4
allow for non-balances panel and non-consecutive data
s3alfisc Sep 6, 2025
13be129
Merge branch 'issue675HAC' of https://github.com/damandhaliwal/pyfixe…
s3alfisc Sep 6, 2025
a5241ad
_dk_meat added. documentation and tests are pending
damandhaliwal Sep 7, 2025
4b66649
fix indentation error & njit & linter & mypy
s3alfisc Sep 7, 2025
4df27e1
prepare tests for non-consecutive implementation
s3alfisc Sep 7, 2025
a62d8ab
good speed ups of time series hac
s3alfisc Sep 7, 2025
707909a
fixed everything and added fixef_maxiter everywhere
damandhaliwal Jun 17, 2025
e23ce56
more numba for DK
s3alfisc Sep 7, 2025
1186af4
code reorg + check for unique time and panel
s3alfisc Sep 8, 2025
f89c6e2
solve merge conflicts
s3alfisc Oct 17, 2025
5a28005
more adjustment to new syntax
s3alfisc Oct 17, 2025
51da551
update get_panel_idx
s3alfisc Oct 17, 2025
e070e30
tests for fepois, glm
s3alfisc Oct 18, 2025
f2cef1b
ar 1 errors
s3alfisc Oct 18, 2025
44e7698
add unrelated test error
s3alfisc Oct 18, 2025
bbadff3
fix test bugs
s3alfisc Oct 18, 2025
69e07d7
compure panel hac on scores, fix bugs for glms, support glms
s3alfisc Oct 18, 2025
8093a8f
update readme
s3alfisc Oct 18, 2025
defb29c
update tests
s3alfisc Oct 18, 2025
fa4c167
fix linter
s3alfisc Oct 18, 2025
4ade3f1
some progress ssc
s3alfisc Oct 18, 2025
aa9f3ed
pre commit
s3alfisc Oct 18, 2025
90f29d4
fix HAC ssc
s3alfisc Oct 19, 2025
020dda7
Merge branch 'master' into ssc-hac
s3alfisc Oct 19, 2025
fdb02c7
fix
s3alfisc Oct 19, 2025
9c6e077
delete print statements
s3alfisc Oct 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 8 additions & 22 deletions pyfixest/estimation/feols_.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def __init__(
self._demean_func = impl["demean"]
self._find_collinear_variables_func = impl["collinear"]
self._crv1_meat_func = impl["crv1_meat"]
self._cound_nested_fixef_func = impl["nonnested"]
self._count_nested_fixef_func = impl["nonnested"]

# set in get_fit()
self._tZX = np.array([])
Expand Down Expand Up @@ -690,32 +690,18 @@ def vcov(
self._vcov = self._ssc * self._vcov_hetero()

elif self._vcov_type == "HAC":
if self._ssc_dict["k_adj"]:
self._ssc_dict["k_adj"] = False
warnings.warn(
"k_adj was set to False for HAC inference because that's currently the only supported option."
)
if self._ssc_dict["G_adj"]:
self._ssc_dict["G_adj"] = False
warnings.warn(
"G_adj was set to False for HAC inference because that's currently the only supported option."
)
if self._ssc_dict["k_fixef"] == "nonnested":
self._ssc_dict["k_fixef"] = "none"
warnings.warn(
"k_fixef was set to none for HAC inference because nonnested is currently not supported."
)

ssc_kwargs_hac = {
"k_fe_nested": 0,
"n_fe_fully_nested": 0,
"k_fe_nested": 0, # nesting ignored / irrelevant for HAC SEs
"n_fe_fully_nested": 0, # nesting ignored / irrelevant for HAC SEs
"vcov_sign": 1,
"vcov_type": "HAC",
"G": self._N,
"G": np.unique(self._data[self._time_id]).shape[
0
], # number of unique time periods T used
}

all_kwargs = {**ssc_kwargs, **ssc_kwargs_hac}
self._ssc, self._dof_k, self._df_t = get_ssc(**all_kwargs)
self._ssc, self._df_k, self._df_t = get_ssc(**all_kwargs)

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

Expand Down Expand Up @@ -770,7 +756,7 @@ def vcov(
k_fe_nested = 0
n_fe_fully_nested = 0
if self._has_fixef and self._ssc_dict["k_fixef"] == "nonnested":
k_fe_nested_flag, n_fe_fully_nested = self._cound_nested_fixef_func(
k_fe_nested_flag, n_fe_fully_nested = self._count_nested_fixef_func(
all_fixef_array=np.array(
self._fixef.replace("^", "_").split("+"), dtype=str
),
Expand Down
5 changes: 2 additions & 3 deletions pyfixest/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def get_ssc(
adj_value = (N - 1) / (N - df_k) if vcov_type != "hetero" else N / (N - df_k)

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

df_t = N - df_k if vcov_type in ["iid", "hetero"] else G - 1

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


Expand Down
10 changes: 0 additions & 10 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,13 +1069,3 @@ def test_errors_hac():
vcov=vcov,
vcov_kwargs={"time_id": "time3", "panel_id": "panel", "lag": 5},
)


def test_errors_hac_inference():
data = pf.get_data()
data["Y"] = np.where(data["Y"] > 0, 1, 0)
with pytest.raises(
NotImplementedError,
match=r"HAC inference is not supported for this model type\.",
):
pf.quantreg("Y ~ X1", data=data, vcov="NW")
14 changes: 8 additions & 6 deletions tests/test_hac_vs_fixest.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ def _get_r_panel_kwargs(time_id, panel_id, lag, inference):
[None, "weights"],
)
@pytest.mark.parametrize("fml", ols_fmls)
@pytest.mark.parametrize("k_adj", [True, False])
@pytest.mark.parametrize("G_adj", [True, False])
@pytest.mark.parametrize("k_fixef", ["none", "nonnested", "full"])
def test_single_fit_feols_hac_panel(
data_panel,
data_time,
Expand All @@ -279,11 +282,10 @@ def test_single_fit_feols_hac_panel(
weights,
fml,
balanced,
k_adj,
G_adj,
k_fixef,
):
k_adj = False
G_adj = False
ssc_ = ssc(k_adj=k_adj, G_adj=G_adj)

lag = vcov_kwargs.get("lag", None)
time_id = vcov_kwargs.get("time_id", None)
panel_id = vcov_kwargs.get("panel_id", None)
Expand All @@ -310,9 +312,9 @@ def test_single_fit_feols_hac_panel(
if inference == "NW"
else fixest.vcov_DK(**r_panel_kwars),
data=data,
ssc=fixest.ssc(k_adj, "nested", False, G_adj, "min", "min"),
**({"weights": ro.Formula(f"~{weights}")} if weights is not None else {}),
panel_time_step=1,
ssc=fixest.ssc(k_adj, k_fixef, False, G_adj, "min", "min"),
)

mod = pf.feols(
Expand All @@ -321,7 +323,7 @@ def test_single_fit_feols_hac_panel(
vcov=inference,
vcov_kwargs=vcov_kwargs,
weights=weights,
ssc=ssc_,
ssc=pf.ssc(k_adj=k_adj, k_fixef=k_fixef, G_adj=G_adj),
)

# r_fixest to global r env, needed for
Expand Down
Loading