Skip to content

Commit 306da82

Browse files
authored
Fix errors arising under formulaic>=1.1.0 (#786)
* add jax benchmark notebook * support formulaic 1.1.0 * delete gpu notebook * delete coverage xml
1 parent cb31ee2 commit 306da82

File tree

8 files changed

+54
-4578
lines changed

8 files changed

+54
-4578
lines changed

coverage.xml

Lines changed: 0 additions & 4503 deletions
This file was deleted.

docs/changelog.qmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Adds a `pf.feglm()` function that supports GLMs with normal and binomial families (gaussian, logit, probit) without fixed effects. Fixed effects support is work in progress.
66
- Adds a function argument `context`, that allows to pass information / context to the `formulaic.Formulaic.get_model_matrix()` call that creates the model matrix.
77
- Fix a bug that caused reindexing of `LPDID._coeftable` when calling `LPDID.iplot()`. As a result, a second call of `LPDID.iplot()` would fail.
8+
- Bumps the required `formulaic` version to `1.1.0` and fixes errors that arose when a) the ref argument was used for i() syntax, which led to a silent failure under formulaic >= 1.1.0, and fixef() / predict() with fixed effects, which led to a loud error.
89

910
## PyFixest 0.27.0
1011

pixi.lock

Lines changed: 27 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyfixest/estimation/model_matrix_fixest_.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ def _get_columns_to_drop_and_check_ivars(
254254
if ref and "_" in ref:
255255
ref = ref.replace("_", "")
256256

257-
pattern = rf"\[T\.{ref}(?:\.0)?\]:{var2}"
257+
pattern = rf"\[(?:T\.)?{ref}(?:\.0)?\]:{var2}"
258258
if ref:
259259
for column in X.columns:
260260
if var1 in column and re.search(pattern, column):

pyfixest/utils/dev_utils.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -182,18 +182,12 @@ def _extract_variable_level(fe_string: str):
182182
A tuple containing the extracted variable and level for the fixed
183183
effect.
184184
"""
185-
c_pattern = r"C\((.+?)\)"
186-
t_pattern = r"\[T\.(.*\])"
187-
c_match = re.search(c_pattern, fe_string)
188-
t_match = re.search(t_pattern, fe_string, re.DOTALL)
185+
pattern = r"C\(([^)]*)\)\[(?:T\.)?(.*)\]$"
186+
match = re.search(pattern, fe_string)
187+
if not match:
188+
raise ValueError(f"Cannot parse: {fe_string}")
189189

190-
if not c_match or not t_match:
191-
raise ValueError(
192-
f"feols() failed after regex encountered the following value as a fixed effect:\n {fe_string}."
193-
+ "\nThis may due to the presence of line separation and/or escape sequences within the string."
194-
+ " If so, consider recoding the underlying string. Otherwise, please open a PR in the github repo!"
195-
)
196-
197-
variable = c_match.group(1)
198-
level = t_match.group(1)
199-
return "C(" + variable + ")", level[0 : level.rfind("]")]
190+
variable = match.group(1)
191+
level = match.group(2)
192+
193+
return f"C({variable})", level

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ license = { text = "MIT" }
1313
dependencies = [
1414
"lets-plot>=4.0.0",
1515
"scipy>=1.6",
16-
"formulaic>=1.0.0,<1.1.0",
16+
"formulaic>=1.1.0",
1717
"pandas>=1.1.0",
1818
"numba>=0.58.0",
1919
"seaborn>=0.13.2",

tests/test_i.py

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -115,42 +115,23 @@ def test_i_vs_fixest():
115115
)
116116

117117

118-
def test_i_interacted_fixest():
118+
@pytest.mark.parametrize(
119+
"fml",
120+
[
121+
"dep_var ~ i(state)",
122+
"dep_var ~ i(state, ref = 1)",
123+
"dep_var ~ i(state, year)",
124+
"dep_var ~ i(state, year, ref = 1)",
125+
"dep_var ~ i(state, year) | state",
126+
"dep_var ~ i(state, year, ref = 1) | state",
127+
],
128+
)
129+
def test_i_interacted_fixest(fml):
119130
df_het = pd.read_csv("pyfixest/did/data/df_het.csv")
120131
df_het["X"] = np.random.normal(df_het.shape[0])
121132

122-
# ------------------------------------------------------------------------ #
123-
# no fixed effects
124-
125-
# no references
126-
fit_py = feols("dep_var~i(state, year)", df_het)
127-
fit_r = fixest.feols(ro.Formula("dep_var~i(state, year)"), df_het)
133+
fit_py = feols(fml, df_het)
134+
fit_r = fixest.feols(ro.Formula(fml), df_het)
128135
np.testing.assert_allclose(
129136
fit_py.coef().values, np.array(fit_r.rx2("coefficients"))
130137
)
131-
132-
if True:
133-
# no reference one fixed effect
134-
fit_py = feols("dep_var~i(state, year) | state ", df_het)
135-
fit_r = fixest.feols(ro.Formula("dep_var~i(state, year) | state"), df_het)
136-
np.testing.assert_allclose(
137-
fit_py.coef().values, np.array(fit_r.rx2("coefficients"))
138-
)
139-
140-
if True:
141-
# one reference
142-
fit_py = feols("dep_var~i(state, year,ref=1) ", df_het)
143-
fit_r = fixest.feols(ro.Formula("dep_var~i(state, year, ref = 1)"), df_het)
144-
np.testing.assert_allclose(
145-
fit_py.coef().values, np.array(fit_r.rx2("coefficients"))
146-
)
147-
148-
if True:
149-
# one reference and fixed effect
150-
fit_py = feols("dep_var~i(state, year,ref=1) | state ", df_het)
151-
fit_r = fixest.feols(
152-
ro.Formula("dep_var~i(state, year, ref = 1) | state"), df_het
153-
)
154-
np.testing.assert_allclose(
155-
fit_py.coef().values, np.array(fit_r.rx2("coefficients"))
156-
)

tests/test_predict_resid_fixef.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,5 @@ def test_extract_variable_level():
305305
assert _extract_variable_level(var) == ("C(f3)", "1.0")
306306
var = "C(f4)[T.1]"
307307
assert _extract_variable_level(var) == ("C(f4)", "1")
308+
var = "C(f5)[1.0]"
309+
assert _extract_variable_level(var) == ("C(f5)", "1.0")

0 commit comments

Comments
 (0)