You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix dof/collinearity reporting and improve API, docs, and errors
Correctness / reporting:
- fit.jl: residual dof no longer subtracts the intercept twice. The reported
dof_residual (and the t-stat p-values, confidence intervals, and F-stat
p-value derived from it) was off by one: N-K-1 instead of N-K for
simple/robust, and G-2 instead of G-1 for cluster-robust SEs. Standard
errors themselves were already correct. Now matches Stata.
- basecol.jl: make rank detection in invsym! scale-invariant. The absolute
tolerance floor of 1 dropped genuinely independent regressors whose total
sum of squares fell below ~sqrt(eps); the new sweep_tolerances helper floors
each column's tolerance by a relative max(diag)*eps and still drops all-zero
columns. Bit-identical on well-scaled designs.
- fixedeffects.jl: nunique no longer counts continuous-interaction FE groups
(fe(id)&x) whose interaction is identically zero, so dof_fes is not
overstated for those groups.
- FixedEffectModel.jl: coeftable labels the confidence-interval columns with
the requested level instead of a hard-coded 95%.
- basecol.jl: simplify reinsert_omitted! (reinsertion of collinear-dropped and
IV-reclassified coefficients).
API, docs, and error messages:
- partial_out: deprecate method=:gpu (mapped to :CUDA) as reg already does;
align the default tol to 1e-6 to match reg, so partial_out and
reg(...; save = :residuals) return the same residuals; fix the method, tol,
and Returns sections of the docstring (it returns a 5-tuple).
- formula.jl: a malformed formula with `~` on the left-hand side now throws a
clear ArgumentError instead of an opaque MethodError.
- add docstrings for the exported fe, has_iv, has_fe, dof_fes, and
FixedEffectModel.
- document the collinear-omitted convention (zero coefficient, NaN standard
error) in the reg and reinsert_omitted! docstrings.
- README: use save = :fe (not the legacy save = true); note that GPU methods
default to Float32.
Tests: residual dof, CI labels, continuous-slope FE dof, small-scale
collinearity, collinear-NaN reporting, malformed IV, partial_out :gpu
deprecation, and reinsert_omitted!.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
reg(df, @formula(Sales ~ NDI +fe(State) +fe(Year)), method =:Metal)
140
140
```
141
141
142
+
GPU methods default to single precision (`Float32`) for the demeaning step. On CUDA you can pass `double_precision = true` to use `Float64`; Metal supports only `Float32`.
143
+
142
144
## Solution method
143
145
Denote the model `y = X β + D θ + e` where X is a matrix with few columns and D is the design matrix from categorical variables. Estimates for `β`, along with their standard errors, are obtained in two steps:
144
146
145
147
1.`y, X` are regressed on `D` using the package [FixedEffects.jl](https://github.com/FixedEffects/FixedEffects.jl)
146
148
2. Estimates for `β`, along with their standard errors, are obtained by regressing the projected `y` on the projected `X` (an application of the Frisch Waugh-Lovell Theorem)
147
-
3. With the option `save = true`, estimates for the high dimensional fixed effects are obtained after regressing the residuals of the full model minus the residuals of the partialed out models on `D` using the package [FixedEffects.jl](https://github.com/FixedEffects/FixedEffects.jl)
149
+
3. With the option `save = :fe` (or `save = :all`), estimates for the high dimensional fixed effects are obtained after regressing the residuals of the full model minus the residuals of the partialed out models on `D` using the package [FixedEffects.jl](https://github.com/FixedEffects/FixedEffects.jl)
Copy file name to clipboardExpand all lines: src/fit.jl
+9-2Lines changed: 9 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -23,6 +23,8 @@ Estimate a linear model with high dimensional categorical variables / instrument
23
23
### Details
24
24
Models with instruments variables are estimated using 2SLS. `reg` tests for weak instruments by computing the Kleibergen-Paap rk Wald F statistic, a generalization of the Cragg-Donald Wald F statistic for non i.i.d. errors. The statistic is similar to the one returned by the Stata command `ivreg2`.
25
25
26
+
Regressors that are collinear with other regressors (or with the fixed effects) are dropped from the estimation. A dropped coefficient is reported as `0` with a `NaN` standard error (and `NaN` t-statistic, p-value, and confidence interval).
27
+
26
28
### Examples
27
29
```julia
28
30
using RDatasets, FixedEffectModels
@@ -325,9 +327,14 @@ function StatsAPI.fit(::Type{FixedEffectModel},
325
327
# Compute Fstat
326
328
F =Fstat(coef, matrix_vcov, has_intercept)
327
329
328
-
# dof_ is the number of estimated coefficients beyond the intercept.
330
+
# dof_ is the number of estimated coefficients beyond the intercept (F-stat numerator).
Copy file name to clipboardExpand all lines: src/partial_out.jl
+13-6Lines changed: 13 additions & 6 deletions
Original file line number
Diff line number
Diff line change
@@ -5,17 +5,19 @@ Partial out variables in a Dataframe
5
5
* `df`: A table
6
6
* `formula::FormulaTerm`: A formula created using `@formula`
7
7
* `add_mean::Bool`: Should the initial mean added to the returned variable?
8
-
* `method::Symbol`: A symbol for the method. Default is :cpu. Alternatively, :gpu requires `CuArrays`. In this case, use the option `double_precision = false` to use `Float32`.
8
+
* `method::Symbol`: A symbol for the method. Default is `:cpu`. Alternatively, use `:CUDA` or `:Metal` (load the respective package — CUDA.jl or Metal.jl — before `using FixedEffectModels`).
9
9
* `maxiter::Integer`: Maximum number of iterations
10
10
* `double_precision::Bool`: Should the demeaning operation use Float64 rather than Float32? Default to true.
11
-
* `tol::Real`: Tolerance
11
+
* `tol::Real`: Tolerance for the iterative demeaning. Default `1e-6`, matching `reg` (so that `partial_out` and `reg(...; save = :residuals)` return the same residuals).
12
12
* `align::Bool`: Should the returned DataFrame align with the original DataFrame in case of missing values? Default to true.
13
13
* `drop_singletons::Bool=false`: Should singletons be dropped?
14
14
15
15
### Returns
16
-
* `::DataFrame`: a dataframe with as many columns as there are dependent variables and as many rows as the original dataframe.
17
-
* `::Vector{Int}`: a vector of iterations for each column
18
-
* `::Vector{Bool}`: a vector of success for each column
16
+
* `::DataFrame`: residuals, one column per dependent variable, aligned with the original dataframe.
17
+
* `::BitVector`: the estimation-sample mask (the rows actually used).
18
+
* `::Vector{Int}`: number of demeaning iterations for each column.
19
+
* `::Vector{Bool}`: convergence flag for each column.
20
+
* `::Int`: degrees of freedom absorbed by the fixed effects.
19
21
20
22
### Details
21
23
`partial_out` returns the residuals of a set of variables after regressing them on a set of regressors. The syntax is similar to `reg` - but it accepts multiple dependent variables. It returns a dataframe with as many columns as there are dependent variables and as many rows as the original dataframe.
has_iv(@nospecialize(f::FormulaTerm)) =any(x -> x isa FormulaTerm, eachterm(f.rhs))
16
16
functionparse_iv(@nospecialize(f::FormulaTerm))
17
+
f.lhs isa FormulaTerm &&throw(ArgumentError("Malformed formula: the left-hand side cannot contain `~`. The instrumental-variable syntax is `y ~ exogenous + (endogenous ~ instruments)`."))
17
18
ifhas_iv(f)
18
19
i =findfirst(x -> x isa FormulaTerm, eachterm(f.rhs))
0 commit comments