Skip to content

Commit 934aae9

Browse files
paldaykleinschmidt
andauthored
Special case division of/by a constant (#7)
* Special case division of/by a constant * patch bump * Update README.md * Update README.md Co-authored-by: Dave Kleinschmidt <dave.f.kleinschmidt@gmail.com>
1 parent 89d0d46 commit 934aae9

File tree

4 files changed

+27
-3
lines changed

4 files changed

+27
-3
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "RegressionFormulae"
22
uuid = "545c379f-4ec2-4339-9aea-38f2fb6a8ba2"
33
authors = ["Dave Kleinschmidt", "Phillip Alday"]
4-
version = "0.1.0"
4+
version = "0.1.1"
55

66
[deps]
77
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ using RegressionFormulae, StatsModels, GLM, DataFrames
2727

2828
`a / b` expands to `a + fulldummy(a) & b`.
2929

30+
Numeric constants are special cased so that `/` performs division, making it possible to e.g. convert time to speed in the formula:
31+
```julia
32+
julia> fit(MyModelType, @formula(time_in_milliseconds / 1000 ~ 1 + x), my_data)
33+
```
34+
3035
### Raising terms to a power ###
3136

3237
Generate all main effects and interactions up to the specified order. For

src/nesting.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function Base.:(/)(outer::InteractionTerm, inner::TermTuple)
4545
end
4646

4747
function Base.:(/)(outer::AbstractTerm, inner::AbstractTerm)
48-
throw(ArgumentError("nesting terms requires categorical grouping term, got $outer / $inner " *
48+
throw(ArgumentError("Nesting terms requires categorical grouping term, got $outer / $inner " *
4949
"Manually specify $outer as `CategoricalTerm` in hints/contrasts"))
5050
end
5151

@@ -57,7 +57,10 @@ function StatsModels.apply_schema(
5757
length(t.args_parsed) == 2 ||
5858
throw(ArgumentError("malformed nesting term: $t (Exactly two arguments required)"))
5959

60+
any(x -> isa(x, ConstantTerm), t.args_parsed) && return t
61+
6062
args = apply_schema.(t.args_parsed, Ref(sch), Mod)
6163

6264
return first(args) / last(args)
6365
end
66+

test/nesting.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ using Test
44

55
include("dummymod.jl")
66

7-
dat = (; y=zeros(3), a=["u","i","o"], b=["q","w","e"], c=["s","d","f"], x=1:3)
7+
# y must not be the multiplicative or additive identity
8+
# otherwise some of the tests will trivially pass
9+
dat = (; y=2*ones(3), a=["u","i","o"], b=["q","w","e"], c=["s","d","f"], x=1:3)
810

911
@testset "error checking" begin
1012
sch = schema(dat)
@@ -13,6 +15,20 @@ dat = (; y=zeros(3), a=["u","i","o"], b=["q","w","e"], c=["s","d","f"], x=1:3)
1315
@test !RegressionFormulae._isfulldummy(term(:a))
1416
end
1517

18+
@testset "division" begin
19+
m = fit(DummyMod, @formula(y / 1000 ~ 1 + x), dat)
20+
@test all(response(m) .≈ dat.y ./ 1000)
21+
22+
m = fit(DummyMod, @formula(1000 / y ~ 1 + x), dat)
23+
@test all(response(m) .≈ 1000 ./ dat.y)
24+
25+
m = fit(DummyMod, @formula(y ~ 1 + 1000 / x), dat)
26+
@test all(m.mm.m[:, 2] .≈ 1000 ./ dat.x)
27+
28+
m = fit(DummyMod, @formula(y ~ 1 + x / 1000), dat)
29+
@test all(m.mm.m[:, 2] .≈ dat.x ./ 1000)
30+
end
31+
1632
@testset "single nesting level" begin
1733
m = fit(DummyMod, @formula(y ~ 0 + a / b), dat)
1834
@test coefnames(m) == ["a: i", "a: o", "a: u",

0 commit comments

Comments
 (0)