Skip to content

Commit 740fab0

Browse files
change tolerance to 1e-6 (#166)
* Update fit.jl * Update fit.jl * Update Project.toml * Create ci.yml * Update README.md * update * update README * Update README.md * rework on Readme
1 parent 7058a4c commit 740fab0

8 files changed

Lines changed: 94 additions & 20 deletions

File tree

.github/workflows/ci.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: CI
2+
on:
3+
pull_request:
4+
branches:
5+
- master
6+
push:
7+
branches:
8+
- master
9+
tags: '*'
10+
jobs:
11+
test:
12+
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
13+
runs-on: ${{ matrix.os }}
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
version:
18+
- '1.3' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
19+
- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
20+
os:
21+
- ubuntu-latest
22+
arch:
23+
- x64
24+
steps:
25+
- uses: actions/checkout@v2
26+
- uses: julia-actions/setup-julia@v1
27+
with:
28+
version: ${{ matrix.version }}
29+
arch: ${{ matrix.arch }}
30+
- uses: actions/cache@v1
31+
env:
32+
cache-name: cache-artifacts
33+
with:
34+
path: ~/.julia/artifacts
35+
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
36+
restore-keys: |
37+
${{ runner.os }}-test-${{ env.cache-name }}-
38+
${{ runner.os }}-test-
39+
${{ runner.os }}-
40+
- uses: julia-actions/julia-buildpkg@v1
41+
- uses: julia-actions/julia-runtest@v1
42+
- uses: julia-actions/julia-processcoverage@v1
43+
- uses: codecov/codecov-action@v1
44+
with:
45+
file: lcov.info
46+

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
benchmark/.sublime2Terminal.jl

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "FixedEffectModels"
22
uuid = "9d5cd8c9-2029-5cab-9928-427838db53e3"
3-
version = "1.5.1"
3+
version = "1.6.0"
44

55
[deps]
66
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"

README.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![Build Status](https://travis-ci.com/FixedEffects/FixedEffectModels.jl.svg?branch=master)](https://travis-ci.com/FixedEffects/FixedEffectModels.jl)
1+
[![Build status](https://github.com/FixedEffects/FixedEffectModels.jl/workflows/CI/badge.svg)](https://github.com/FixedEffects/FixedEffectModels.jl/actions)
22

33
This package estimates linear models with high dimensional categorical variables and/or instrumental variables.
44

@@ -11,7 +11,7 @@ The objective of the package is similar to the Stata command [`reghdfe`](https:/
1111
![benchmark](http://www.matthieugomez.com/files/fixedeffectmodels_benchmark.png)
1212

1313

14-
Performances are roughly similar to the newer R function [`feols`](https://cran.r-project.org/web/packages/fixest/fixest.pdf) (note: use `tol = 1e-6, drop_singletons = false` to match the default options of `feols`). The main difference is that `FixedEffectModels` can also run the demeaning operation on a GPU (with `method = :gpu`).
14+
Performances are roughly similar to the newer R function [`feols`](https://cran.r-project.org/web/packages/fixest/fixest.pdf). The main difference is that `FixedEffectModels` can also run the demeaning operation on a GPU (with `method = :gpu`).
1515

1616
## Syntax
1717

@@ -55,23 +55,29 @@ reg(df, @formula(Sales ~ NDI + fe(State) + fe(Year)), Vcov.cluster(:State), weig
5555
reg(df, term(:Sales) ~ term(:NDI) + fe(:State) + fe(:Year))
5656
```
5757

58-
- Standard errors are indicated with the prefix `Vcov` (with the package [Vcov](http://github.com/matthieugomez/Vcov.jl))
58+
- The option `contrasts` specifies that a column should be understood as a set of dummy variables:
5959
```julia
60-
Vcov.robust()
61-
Vcov.cluster(:State)
62-
Vcov.cluster(:State, :Year)
60+
reg(df, @formula(Sales ~ Price + Year); contrasts = Dict(:Year => DummyCoding()))
6361
```
62+
You can specify different base levels
63+
```julia
64+
reg(df, @formula(Sales ~ Price + Year); contrasts = Dict(:Year => DummyCoding(base = 80)))
65+
```
66+
67+
6468
- The option `weights` specifies a variable for weights
6569
```julia
6670
weights = :Pop
6771
```
68-
- The option `contrasts` specifies particular contrasts for a dummy variable in the formula, e.g.
72+
73+
- Standard errors are indicated with the prefix `Vcov` (with the package [Vcov](http://github.com/matthieugomez/Vcov.jl))
6974
```julia
70-
reg(df, @formula(Sales ~ Year); contrasts = Dict(:Year => DummyCoding(base = 80)))
75+
Vcov.robust()
76+
Vcov.cluster(:State)
77+
Vcov.cluster(:State, :Year)
7178
```
72-
- The option `save` can be set to one of the following: `none` (default) to save nothing `:residuals` to save residuals, `:fe` to save fixed effects. You can access the result with `residuals()` and `fe()`
7379

74-
-
80+
- The option `save` can be set to one of the following: `none` (default) to save nothing `:residuals` to save residuals, `:fe` to save fixed effects. You can access the result with `residuals()` and `fe()`
7581

7682
- The option `method` can be set to one of the following: `:cpu`, `:gpu` (see Performances below).
7783

@@ -93,10 +99,11 @@ You may use [RegressionTables.jl](https://github.com/jmboehm/RegressionTables.jl
9399

94100
## Performances
95101

96-
### MultiThreads
97-
By default, `FixedEffectModels` uses as many threads as `Threads.nthreads()`. Use the option `nthreads` to select the number of threads to use in the estimation. Default to `Threads.nthreads()`.
98102

103+
### MultiThreads
104+
`FixedEffectModels` is multi-threaded. Use the option `nthreads` to select the number of threads to use in the estimation (defaults to `Threads.nthreads()`). That being said, multithreading does not usually make a big difference.
99105

106+
### GPU
100107
The package has support for GPUs (Nvidia) (thanks to Paul Schrimpf). This can make the package an order of magnitude faster for complicated problems.
101108

102109
To use GPU, run `using CUDA` before `using FixedEffectModels`. Then, estimate a model with `method = :gpu`. For maximum speed, set the floating point precision to `Float32` with `double_precision = false`.

benchmark/benchmark.jl

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using DataFrames, FixedEffectModels, Random
1+
using DataFrames, FixedEffectModels, Random, CategoricalArrays
22

33
# Very simple setup
44
N = 10000000
@@ -17,9 +17,9 @@ df = DataFrame(id1 = id1, id2 = id2, x1 = x1, x2 = x2, y = y)
1717
@time reg(df, @formula(y ~ x1 + x2), Vcov.cluster(:id2))
1818
# 0.759032 seconds (631 allocations: 728.437 MiB, 4.89% gc time)
1919
@time reg(df, @formula(y ~ x1 + x2 + fe(id1)))
20-
# 1.452800 seconds (231.14 k allocations: 908.415 MiB, 3.40% gc time, 9.72% compilation time)
20+
# 1.316560 seconds (230.66 k allocations: 908.386 MiB, 4.28% gc time, 11.84% compilation time)
2121
@time reg(df, @formula(y ~ x1 + x2 + fe(id1)), Vcov.cluster(:id1))
22-
# 1.785358 seconds (231.43 k allocations: 952.060 MiB, 8.22% compilation time)
22+
# 1.501165 seconds (230.94 k allocations: 952.029 MiB, 0.84% gc time, 10.66% compilation time)
2323
@time reg(df, @formula(y ~ x1 + x2 + fe(id1) + fe(id2)))
2424
# 3.058090 seconds (331.56 k allocations: 1.005 GiB, 2.08% gc time, 5.89% compilation time)
2525

@@ -35,6 +35,22 @@ x2 = cos.(id1) + sin.(id2) + randn(N)
3535
y= 3 .* x1 .+ 5 .* x2 .+ cos.(id1) .+ cos.(id2).^2 .+ randn(N)
3636
df = DataFrame(id1 = id1, id2 = id2, x1 = x1, x2 = x2, y = y)
3737
@time reg(df, @formula(y ~ x1 + x2 + fe(id1) + fe(id2)))
38-
# 15.738214 seconds (929.97 k allocations: 147.737 MiB, 0.95% gc time)
38+
# 3.190288 seconds (393.89 k allocations: 109.614 MiB, 3.63% gc time, 9.75% compilation time)
3939

4040

41+
+# fixest
42+
n = 10_000_000
43+
nb_dum = [div(n,20), floor(Int, sqrt(n)), floor(Int, n^.33)]
44+
N = nb_dum.^3
45+
id1 = categorical(rand(1:nb_dum[1], n))
46+
id2 = categorical(rand(1:nb_dum[2], n))
47+
id3 = categorical(rand(1:nb_dum[3], n))
48+
X1 = rand(n)
49+
ln_y = 3 .* X1 .+ rand(n)
50+
df = DataFrame(X1 = X1, ln_y = ln_y, id1 = id1, id2 = id2, id3 = id3)
51+
@time reg(df, @formula(ln_y ~ X1 + fe(id1)), Vcov.cluster(:id1))
52+
# 1.058386 seconds (219.24 k allocations: 751.826 MiB, 13.05% compilation time)
53+
@time reg(df, @formula(ln_y ~ X1 + fe(id1) + fe(id2)), Vcov.cluster(:id1))
54+
# 2.895223 seconds (284.28 k allocations: 870.782 MiB, 1.73% gc time, 5.97% compilation time)
55+
@time reg(df, @formula(ln_y ~ X1 + fe(id1) + fe(id2) + fe(id3)), Vcov.cluster(:id1))
56+
# 3.191483 seconds (380.10 k allocations: 991.134 MiB, 6.88% compilation time)

src/FixedEffectModel.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ function top(x::FixedEffectModel)
191191
out = vcat(out,
192192
["R2 within" format_scientific(x.r2_within);
193193
"Iterations" sprint(show, x.iterations, context = :compact => true);
194-
"Converged" sprint(show, x.converged, context = :compact => true);
195194
])
196195
end
197196
return out

src/fit.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Estimate a linear model with high dimensional categorical variables / instrument
1313
* `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`.
1414
* `nthreads::Integer` Number of threads to use in the estimation. If `method = :cpu`, defaults to `Threads.nthreads()`. If `method = :gpu`, defaults to 256.
1515
* `double_precision::Bool`: Should the demeaning operation use Float64 rather than Float32? Default to true.
16-
* `tol::Real` Tolerance. Default to 1e-8 if `double_precision = true`, 1e-6 otherwise.
16+
* `tol::Real` Tolerance. Default to 1e-6.
1717
* `maxiter::Integer = 10000`: Maximum number of iterations
1818
* `drop_singletons::Bool = true`: Should singletons be dropped?
1919
* `progress_bar::Bool = true`: Should the regression show a progressbar
@@ -50,7 +50,7 @@ function reg(
5050
@nospecialize(method::Symbol = :cpu),
5151
@nospecialize(nthreads::Integer = method == :cpu ? Threads.nthreads() : 256),
5252
@nospecialize(double_precision::Bool = true),
53-
@nospecialize(tol::Real = double_precision ? 1e-8 : 1e-6),
53+
@nospecialize(tol::Real = 1e-6),
5454
@nospecialize(maxiter::Integer = 10000),
5555
@nospecialize(drop_singletons::Bool = true),
5656
@nospecialize(progress_bar::Bool = true),
@@ -447,5 +447,6 @@ function reg(
447447
if esample == Colon()
448448
esample = trues(N)
449449
end
450+
450451
return FixedEffectModel(coef, matrix_vcov, vcov, nclusters, esample, residuals2, augmentdf, fekeys, coef_names, response_name, formula_origin, formula, contrasts, nobs, dof_residual_, rss, tss_total, r2, adjr2, F, p, iterations, converged, r2_within, F_kp, p_kp)
451452
end

test/fit.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ x = reg(df, m)
7979
m = @formula y ~ z1 + x1&x2&fe(id1)
8080
x = reg(df, m)
8181
@test coef(x) [122.98713, 0.30933] atol = 1e-4
82+
# SSR does not work well here
8283
m = @formula y ~ z1 + (x1&x2)*fe(id1)
8384
x = reg(df, m)
8485
@test coef(x) [0.421406, 0.0] atol = 1e-4
@@ -612,11 +613,13 @@ x = reg(df, m, weights = :w)
612613
@test coef(x) [- 0.043475472188120416] atol = 1e-3
613614

614615
## the last two ones test an ill conditioned model matrix
616+
# SSR does not work well here
615617
m = @formula y ~ x1 + fe(id1) + fe(id1)&id2
616618
x = reg(df, m)
617619
@test coef(x) [- 0.122354] atol = 1e-4
618620
@test x.iterations <= 30
619621

622+
# SSR does not work well here
620623
m = @formula y ~ x1 + fe(id1) + fe(id1)&id2
621624
x = reg(df, m, weights = :w)
622625
@test coef(x) [- 0.11752306001586807] atol = 1e-4

0 commit comments

Comments
 (0)