-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathterms.jl
More file actions
255 lines (210 loc) · 8.22 KB
/
Copy pathterms.jl
File metadata and controls
255 lines (210 loc) · 8.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
function mimestring(mime::Type{<:MIME}, x)
buf=IOBuffer()
show(buf, mime(), x)
String(take!(buf))
end
mimestring(x) = mimestring(MIME"text/plain", x)
struct MultiTerm <: AbstractTerm
terms::StatsModels.TupleTerm
end
StatsModels.apply_schema(mt::MultiTerm, sch::StatsModels.Schema, Mod::Type) =
apply_schema.(mt.terms, Ref(sch), Mod)
@testset "terms" begin
using Statistics
@testset "concrete_term" begin
t = term(:aaa)
ts = term("aaa")
@test t == ts
@test string(t) == "aaa"
@test mimestring(t) == "aaa(unknown)"
t0 = concrete_term(t, [3, 2, 1])
@test string(t0) == "aaa"
@test mimestring(t0) == "aaa(continuous)"
@test t0.mean == 2.0
@test t0.var == var([1,2,3])
@test t0.min == 1.0
@test t0.max == 3.0
@test t0 == concrete_term(t, [3, 2, 1])
@test hash(t0) == hash(concrete_term(t, [3, 2, 1]))
t1 = concrete_term(t, [:a, :b, :c])
@test t1.contrasts isa StatsModels.ContrastsMatrix{DummyCoding}
@test string(t1) == "aaa"
@test mimestring(t1) == "aaa(DummyCoding:3→2)"
@test t1 == concrete_term(t, [:a, :b, :c])
@test t1 !== concrete_term(t, [:a, :b, :c])
@test hash(t1) == hash(concrete_term(t, [:a, :b, :c]))
t3 = concrete_term(t, [:a, :b, :c], DummyCoding())
@test t3.contrasts isa StatsModels.ContrastsMatrix{DummyCoding}
@test string(t3) == "aaa"
@test mimestring(t3) == "aaa(DummyCoding:3→2)"
@test t1 == t3
@test hash(t1) == hash(t3)
t2 = concrete_term(t, [:a, :a, :b], EffectsCoding())
@test t2.contrasts isa StatsModels.ContrastsMatrix{EffectsCoding}
@test mimestring(t2) == "aaa(EffectsCoding:2→1)"
@test string(t2) == "aaa"
@test t2 == concrete_term(t, [:a, :a, :b], EffectsCoding())
@test t1 != t2
t2full = concrete_term(t, [:a, :a, :b], StatsModels.FullDummyCoding())
@test t2full.contrasts isa StatsModels.ContrastsMatrix{StatsModels.FullDummyCoding}
@test mimestring(t2full) == "aaa(StatsModels.FullDummyCoding:2→2)"
@test string(t2full) == "aaa"
@test t1 != t2full
end
@testset "term operators" begin
a = term(:a)
b = term(:b)
@test a + b == (a, b)
@test (a ~ b) == FormulaTerm(a, b)
@test string(a~b) == "$a ~ $b"
@test mimestring(a~b) ==
"""FormulaTerm
Response:
a(unknown)
Predictors:
b(unknown)"""
@test mimestring(a ~ term(1) + b) ==
"""FormulaTerm
Response:
a(unknown)
Predictors:
1
b(unknown)"""
@test a & b == InteractionTerm((a,b))
@test string(a & b) == "$a & $b"
@test mimestring(a & b) == "a(unknown) & b(unknown)"
c = term(:c)
ab = a+b
bc = b+c
abc = a+b+c
@test ab+c == abc
@test ab+a == ab
@test a+bc == abc
@test b+ab == ab
@test ab+ab == ab
@test ab+bc == abc
@test sum((a,b,c)) == abc
@test sum((a,)) == a
@test +a == a
end
@testset "expand nested tuples of terms during apply_schema" begin
sch = schema((a=rand(10), b=rand(10), c=rand(10)))
# nested tuples of terms are expanded by apply_schema
terms = (term(:a), (term(:b), term(:c)))
terms2 = apply_schema(terms, sch, Nothing)
@test terms2 isa NTuple{3, ContinuousTerm}
@test terms2 == apply_schema(term.((:a, :b, :c)), sch, Nothing)
# a term that generates multiple terms after apply_schema
mterms = (terms[1], MultiTerm(terms[2]))
terms3 = apply_schema(mterms, sch, Nothing)
@test terms2 == terms3
end
@testset "Intercept and response traits" begin
has_responses = [term(:y), term(1), InterceptTerm{true}(), term(:y)+term(:z),
term(:y) + term(0), term(:y) + InterceptTerm{false}()]
no_responses = [term(0), InterceptTerm{false}()]
has_intercepts = [term(1), InterceptTerm{true}()]
omits_intercepts = [term(0), term(-1), InterceptTerm{false}()]
using StatsModels: hasresponse, hasintercept, omitsintercept
a = term(:a)
for lhs in has_responses, rhs in has_intercepts
@test hasresponse(lhs ~ rhs)
@test hasintercept(lhs ~ rhs)
@test !omitsintercept(lhs ~ rhs)
@test hasresponse(lhs ~ rhs + a)
@test hasintercept(lhs ~ rhs + a)
@test !omitsintercept(lhs ~ rhs + a)
end
for lhs in no_responses, rhs in has_intercepts
@test !hasresponse(lhs ~ rhs)
@test hasintercept(lhs ~ rhs)
@test !omitsintercept(lhs ~ rhs)
@test !hasresponse(lhs ~ rhs + a)
@test hasintercept(lhs ~ rhs + a)
@test !omitsintercept(lhs ~ rhs + a)
end
for lhs in has_responses, rhs in omits_intercepts
@test hasresponse(lhs ~ rhs)
@test !hasintercept(lhs ~ rhs)
@test omitsintercept(lhs ~ rhs)
@test hasresponse(lhs ~ rhs + a)
@test !hasintercept(lhs ~ rhs + a)
@test omitsintercept(lhs ~ rhs + a)
end
for lhs in no_responses, rhs in omits_intercepts
@test !hasresponse(lhs ~ rhs)
@test !hasintercept(lhs ~ rhs)
@test omitsintercept(lhs ~ rhs)
@test !hasresponse(lhs ~ rhs + a)
@test !hasintercept(lhs ~ rhs + a)
@test omitsintercept(lhs ~ rhs + a)
end
end
@testset "equality of function terms" begin
# for now, we use `@formula` to construct the function terms
f1 = @formula(0 ~ (1 | x)).rhs
f2 = @formula(0 ~ (1 | x)).rhs
@test f1 !== f2
@test f1 == f2
@test hash(f1) == hash(f2)
f3 = @formula(0 ~ (1 % x)).rhs
@test f1 != f3
@test hash(f1) != hash(f3)
f4 = @formula(0 ~ (x | 1)).rhs
@test f1 != f4
@test hash(f1) != hash(f4)
f5 = @formula(0 ~ (1 & y | x)).rhs
@test f1 != f5
@test hash(f1) != hash(f5)
ff1 = @formula(y ~ 1 + x + x & y + (1 + x | g))
ff2 = @formula(y ~ 1 + x + x & y + (1 + x | g))
@test ff1 == ff2
@test hash(ff1) == hash(ff2)
end
@testset "uniqueness of FunctionTerms" begin
f1 = @formula(y ~ lag(x,1) + lag(x,1))
f2 = @formula(y ~ lag(x,1))
f3 = @formula(y ~ lag(x,1) + lag(x,2))
@test f1.rhs == f2.rhs
@test f1.rhs != f3.rhs
## addition of two identical function terms
@test f2.rhs + f2.rhs == f2.rhs
end
@testset "Tuple terms" begin
using StatsModels: TermOrTerms, TupleTerm, Term
a, b, c = Term.((:a, :b, :c))
# TermOrTerms - one or more AbstractTerms (if more, a tuple)
# empty tuples are never terms
@test !(() isa TermOrTerms)
@test (a, ) isa TermOrTerms
@test (a, b) isa TermOrTerms
@test (a, b, a&b) isa TermOrTerms
@test !(((), a) isa TermOrTerms)
# can't contain further tuples
@test !((a, (a,), b) isa TermOrTerms)
# a tuple of AbstractTerms OR Tuples of one or more terms
# empty tuples are never terms
@test !(() isa TupleTerm)
@test (a, ) isa TupleTerm
@test (a, b) isa TupleTerm
@test (a, b, a&b) isa TupleTerm
@test !(((), a) isa TupleTerm)
@test (((a,), a) isa TupleTerm)
# no methods for operators on term and empty tuple (=no type piracy)
@test_throws MethodError a + ()
@test_throws MethodError () + a
@test_throws MethodError a & ()
@test_throws MethodError () & a
@test_throws MethodError a ~ ()
@test_throws MethodError () ~ a
# show methods of empty tuples preserved
@test "$(())" == "()"
@test "$((a,b))" == "a + b"
@test "$((a, ()))" == "(a, ())"
end
@testset "concrete_term error messages" begin
t = (a = [1, 2, 3], b = [0.0, 0.5, 1.0])
@test Tables.istable(t)
@test_throws ArgumentError concrete_term(term(:not_there), t )
end
end