@@ -10,6 +10,7 @@ Partial out variables in a Dataframe
1010* `double_precision::Bool`: Should the demeaning operation use Float64 rather than Float32? Default to true.
1111* `tol::Real`: Tolerance
1212* `align::Bool`: Should the returned DataFrame align with the original DataFrame in case of missing values? Default to true.
13+ * `drop_singletons::Bool=false`: Should singletons be dropped?
1314
1415### Returns
1516* `::DataFrame`: a dataframe with as many columns as there are dependent variables and as many rows as the original dataframe.
@@ -40,7 +41,8 @@ function partial_out(
4041 @nospecialize (method:: Symbol = :cpu ),
4142 @nospecialize (double_precision:: Bool = true ),
4243 @nospecialize (tol:: Real = double_precision ? 1e-8 : 1e-6 ),
43- @nospecialize (align = true ))
44+ @nospecialize (align = true ),
45+ @nospecialize (drop_singletons = false ))
4446
4547 # Normalize generic Tables.jl input once; the rest of the function uses
4648 # DataFrame indexing/views, and copycols = false avoids copies when possible.
@@ -73,6 +75,7 @@ function partial_out(
7375 fes, ids, ids_fes = parse_fixedeffect (df, formula_fes)
7476 has_fes = ! isempty (fes)
7577
78+ drop_singletons && drop_singletons! (esample, fes, Threads. nthreads ())
7679
7780 nobs = sum (esample)
7881 (nobs > 0 ) || throw (ArgumentError (" sample is empty" ))
@@ -140,6 +143,8 @@ function partial_out(
140143 residuals .= residuals .+ m
141144 end
142145
146+ dof_fes = mapreduce (nunique, + , fes, init= 0 )
147+
143148 # Return a dataframe
144149 out = DataFrame ()
145150
@@ -151,5 +156,5 @@ function partial_out(
151156 out[! , Symbol (y)] = residuals[:, j]
152157 end
153158 end
154- return out, esample, iterations, convergeds
159+ return out, esample, iterations, convergeds, dof_fes
155160end
0 commit comments