Skip to content

Commit

Permalink
PyFixest 0.8.10.1: add black workflow (#207)
Browse files Browse the repository at this point in the history
* add black action

* run black

* revert notebook
  • Loading branch information
s3alfisc authored Nov 5, 2023
1 parent c4d1e41 commit b5d37d7
Show file tree
Hide file tree
Showing 22 changed files with 561 additions and 464 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/black.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Lint

on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable
48 changes: 26 additions & 22 deletions docs/tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,7 @@
],
"source": [
"from pyfixest.summarize import etable\n",
"\n",
"etable([fit, fit2])"
]
},
Expand Down Expand Up @@ -1273,6 +1274,7 @@
],
"source": [
"from lets_plot import *\n",
"\n",
"LetsPlot.setup_html()\n",
"\n",
"multi_fit.coefplot().show()"
Expand Down Expand Up @@ -1445,7 +1447,7 @@
"import numpy as np\n",
"from pyfixest.experimental.did import did2s\n",
"\n",
"file_path = '../pyfixest/experimental/data/df_het.csv'\n",
"file_path = \"../pyfixest/experimental/data/df_het.csv\"\n",
"df_het = pd.read_csv(file_path)\n",
"df_het.head()"
]
Expand Down Expand Up @@ -1555,22 +1557,24 @@
"source": [
"fit_did2s = did2s(\n",
" df_het,\n",
" yname = \"dep_var\",\n",
" first_stage = \"~ 0 | state + year\",\n",
" second_stage = \"~i(rel_year)\",\n",
" treatment = \"treat\",\n",
" cluster = \"state\",\n",
" i_ref1 = [-1.0, np.inf],\n",
" yname=\"dep_var\",\n",
" first_stage=\"~ 0 | state + year\",\n",
" second_stage=\"~i(rel_year)\",\n",
" treatment=\"treat\",\n",
" cluster=\"state\",\n",
" i_ref1=[-1.0, np.inf],\n",
")\n",
"\n",
"fit_twfe = feols(\n",
" \"dep_var ~ i(rel_year) | state + year\",\n",
" df_het,\n",
" i_ref1 = [-1.0, np.inf],\n",
" vcov = {\"CRV1\": \"state\"}\n",
" i_ref1=[-1.0, np.inf],\n",
" vcov={\"CRV1\": \"state\"},\n",
")\n",
"\n",
"iplot([fit_did2s, fit_twfe], coord_flip=False, figsize = (900, 400), title = \"TWFE vs DID2S\")"
"iplot(\n",
" [fit_did2s, fit_twfe], coord_flip=False, figsize=(900, 400), title=\"TWFE vs DID2S\"\n",
")"
]
},
{
Expand Down Expand Up @@ -1601,21 +1605,21 @@
"from pyfixest.summarize import etable\n",
"\n",
"fit_twfe = event_study(\n",
" data = df_het,\n",
" yname = \"dep_var\",\n",
" idname= \"state\",\n",
" tname = \"year\",\n",
" gname = \"g\",\n",
" estimator = \"twfe\"\n",
" data=df_het,\n",
" yname=\"dep_var\",\n",
" idname=\"state\",\n",
" tname=\"year\",\n",
" gname=\"g\",\n",
" estimator=\"twfe\",\n",
")\n",
"\n",
"fit_did2s = event_study(\n",
" data = df_het,\n",
" yname = \"dep_var\",\n",
" idname= \"state\",\n",
" tname = \"year\",\n",
" gname = \"g\",\n",
" estimator = \"did2s\"\n",
" data=df_het,\n",
" yname=\"dep_var\",\n",
" idname=\"state\",\n",
" tname=\"year\",\n",
" gname=\"g\",\n",
" estimator=\"did2s\",\n",
")\n",
"\n",
"etable([fit_twfe, fit_did2s])"
Expand Down
10 changes: 5 additions & 5 deletions pyfixest/FixestMulti.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,10 @@ def _estimate_all_models(
na_index,
na_index_str,
_icovars,
X_is_empty
) = model_matrix_fixest(fml=fml, data=_data, i_ref1 = _i_ref1, i_ref2 = _i_ref2)
X_is_empty,
) = model_matrix_fixest(
fml=fml, data=_data, i_ref1=_i_ref1, i_ref2=_i_ref2
)

weights = np.ones((Y.shape[0], 1))

Expand Down Expand Up @@ -250,7 +252,7 @@ def _estimate_all_models(
X=Xd,
weights=weights,
coefnames=coefnames,
collin_tol=collin_tol
collin_tol=collin_tol,
)

# special case: sometimes it is useful to fit models as "Y ~ 0 | f1 + f2" to demean Y and to use the predict() method
Expand Down Expand Up @@ -310,7 +312,6 @@ def _estimate_all_models(
# FEOLS.split_log = x

if not FIT._X_is_empty:

# inference
vcov_type = _get_vcov_type(vcov, fval)
FIT.vcov(vcov=vcov_type)
Expand Down Expand Up @@ -640,7 +641,6 @@ def get_fml(
str: The formula string for the regression.
"""


fml = f"{depvar} ~ {covar}"

if endogvars is not None:
Expand Down
18 changes: 4 additions & 14 deletions pyfixest/demean.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from typing import Any, Sequence, Optional



def demean_model(
Y: pd.DataFrame,
X: pd.DataFrame,
Expand Down Expand Up @@ -39,7 +38,6 @@ def demean_model(
- Id (pd.DataFrame or None): A DataFrame of the demeaned Instruments. None if no IV.
"""


YX = pd.concat([Y, X], axis=1)

yx_names = YX.columns
Expand Down Expand Up @@ -91,11 +89,9 @@ def demean_model(
# not data demeaned yet for NA combination

if drop_singletons:

dropped_singleton_indices = _detect_singletons(fe)

if np.any(dropped_singleton_indices == True):

print(
np.sum(dropped_singleton_indices),
"observations are dropped due to singleton fixed effects.",
Expand All @@ -107,7 +103,7 @@ def demean_model(

weights = np.ones(YX.shape[0])

YX_demeaned, success = demean(x = YX, flist = fe, weights = weights)
YX_demeaned, success = demean(x=YX, flist=fe, weights=weights)
if success == False:
raise ValueError("Demeaning failed after 100_000 iterations.")

Expand Down Expand Up @@ -176,7 +172,7 @@ def demean(
x: np.ndarray,
flist: np.ndarray,
weights: np.ndarray,
tol: float = 1e-08, # note: fixest uses 1e-06, but potentially different tolerance criterion
tol: float = 1e-08, # note: fixest uses 1e-06, but potentially different tolerance criterion
maxiter: int = 100_000,
) -> Tuple[np.ndarray, bool]:
n_samples, n_features = x.shape
Expand Down Expand Up @@ -228,9 +224,8 @@ def demean(
return (res, success)


#@nb.jit(nopython=False)
# @nb.jit(nopython=False)
def _detect_singletons(ids):

"""
Detect singleton fixed effects
Args:have a
Expand All @@ -249,7 +244,6 @@ def _detect_singletons(ids):

while True:
for x in range(k):

col = ids[:, x]
col_tmp = ids_tmp[:, x]

Expand All @@ -263,18 +257,14 @@ def _detect_singletons(ids):
singleton_idx[np.isin(col, idx)] = True
singleton_idx_tmp[np.isin(col_tmp, idx)] = True

ids_tmp = ids_tmp[~singleton_idx_tmp,:].astype(ids_tmp.dtype)
ids_tmp = ids_tmp[~singleton_idx_tmp, :].astype(ids_tmp.dtype)
singleton_idx_tmp = singleton_idx_tmp[~singleton_idx_tmp]

break


if np.array_equal(singleton_idx_tmp, singleton_idx_tmp_old):
break

singleton_idx_tmp_old = singleton_idx_tmp.copy()




return singleton_idx
12 changes: 8 additions & 4 deletions pyfixest/estimation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def feols(
fixef_rm: str = "none",
collin_tol: float = 1e-10,
i_ref1: Optional[Union[list, str]] = None,
i_ref2: Optional[Union[list, str]] = None
i_ref2: Optional[Union[list, str]] = None,
) -> Union[Feols, FixestMulti]:
"""
Expand Down Expand Up @@ -156,7 +156,7 @@ def fepois(
iwls_maxiter: int = 25,
collin_tol: float = 1e-10,
i_ref1: Optional[Union[list, str]] = None,
i_ref2: Optional[Union[list, str]] = None
i_ref2: Optional[Union[list, str]] = None,
) -> Union[Fepois, FixestMulti]:
"""
# fepois
Expand Down Expand Up @@ -303,8 +303,12 @@ def _estimation_input_checks(fml, data, vcov, ssc, fixef_rm, collin_tol, i_ref1)
if not collin_tol < 1:
raise ValueError("collin_tol must be less than one")

assert i_ref1 is None or isinstance(i_ref1, (list, str, int, bool, float)), "i_ref1 must be either None, a list, string, int, bool, or float"
assert i_ref1 is None or isinstance(
i_ref1, (list, str, int, bool, float)
), "i_ref1 must be either None, a list, string, int, bool, or float"
# check that if i_ref1 is a list, all elements are of the same type
if isinstance(i_ref1, list):
assert len(i_ref1) > 0, "i_ref1 must not be an empty list"
assert all(isinstance(x, type(i_ref1[0])) for x in i_ref1), "i_ref1 must be a list of elements of the same type"
assert all(
isinstance(x, type(i_ref1[0])) for x in i_ref1
), "i_ref1 must be a list of elements of the same type"
4 changes: 3 additions & 1 deletion pyfixest/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ class NonConvergenceError(Exception):
class MatrixNotFullRankError(Exception):
pass


class EmptyDesignMatrixError(Exception):
pass


class InvalidReferenceLevelError(Exception):
pass
pass
6 changes: 4 additions & 2 deletions pyfixest/experimental/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
print("""
print(
"""
You have loaded the 'pyfixest.experimental' module. Code in this module is not yet stable and
may change in the future. It may also not be sufficiently unit tested (if at all) or documented.
""")
"""
)
Loading

0 comments on commit b5d37d7

Please sign in to comment.