Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "StatsModels"
uuid = "3eaba693-59b7-5ba5-a881-562e759f1c8d"
version = "0.7.10"
version = "0.8.0"

[deps]
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/formula.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ symbols or strings (`Term`) and numbers (`ConstantTerm`), which makes it easy to
work with collections of mixed type:

```jldoctest 1
julia> ts = term.((1, :a, "b"))
julia> ts = term.([1, :a, "b"])
1
a(unknown)
b(unknown)
Expand Down
26 changes: 13 additions & 13 deletions docs/src/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,27 @@ expression returned by the `@formula` macro is evaluated. At this point, the
julia> using StatsModels;

julia> dump(Term(:a) & Term(:b))
InteractionTerm{Tuple{Term, Term}}
terms: Tuple{Term, Term}
InteractionTerm
terms: Array{AbstractTerm}((2,))
1: Term
sym: Symbol a
2: Term
sym: Symbol b

julia> dump(Term(:a) + Term(:b))
Tuple{Term, Term}
Array{AbstractTerm}((2,))
1: Term
sym: Symbol a
2: Term
sym: Symbol b

julia> dump(Term(:y) ~ Term(:a))
FormulaTerm{Term, Term}
FormulaTerm{Term, Vector{AbstractTerm}}
lhs: Term
sym: Symbol y
rhs: Term
sym: Symbol a
rhs: Array{AbstractTerm}((1,))
1: Term
sym: Symbol a
```

!!! note
Expand Down Expand Up @@ -254,7 +255,7 @@ terms:

* `Term`s become `ContinuousTerm`s or `CategoricalTerm`s
* `ConstantTerm`s become `InterceptTerm`s
* Tuples of terms become [`MatrixTerm`](@ref)s where appropriate to explicitly indicate
* Vectors of terms become [`MatrixTerm`](@ref)s where appropriate to explicitly indicate
they should be concatenated into a single model matrix
* Any model-specific (context-specific) interpretation of the terms is made, including
transforming calls to functions that have special meaning in particular
Expand All @@ -274,7 +275,7 @@ Predictors:
b(unknown) & c(unknown)

julia> typeof(f)
FormulaTerm{Term, Tuple{ConstantTerm{Int64}, Term, Term, Term, InteractionTerm{Tuple{Term, Term}}}}
FormulaTerm{Term, Vector{AbstractTerm}}

julia> f = apply_schema(f, schema(f, df))
FormulaTerm
Expand All @@ -288,7 +289,7 @@ Predictors:
b(continuous) & c(DummyCoding:3→2)

julia> typeof(f)
FormulaTerm{ContinuousTerm{Float64}, MatrixTerm{Tuple{InterceptTerm{true}, ContinuousTerm{Float64}, ContinuousTerm{Float64}, CategoricalTerm{DummyCoding, Matrix{Float64}, 2}, InteractionTerm{Tuple{ContinuousTerm{Float64}, CategoricalTerm{DummyCoding, Matrix{Float64}, 2}}}}}}
FormulaTerm{ContinuousTerm{Float64}, MatrixTerm}
```

This transformation is done by calling `apply_schema(term, schema, modeltype)`
Expand Down Expand Up @@ -575,9 +576,8 @@ julia> poly(my_col, my_degree)
poly(a, 3)

julia> poly.([:a, :b], my_degree)
2-element Vector{PolyTerm{Term, ConstantTerm{Int64}}}:
poly(a, 3)
poly(b, 3)
poly(a, 3)
poly(b, 3)
```

These run-time `PolyTerm`s are "schema-less" though, and to be able to construct
Expand Down Expand Up @@ -610,7 +610,7 @@ regression as above (which used `@formula(y ~ 1 + poly(a, 2) + poly(b, 2)`), but
with the predictor names and the polynomial degree stored in variables:

```jldoctest 1
julia> poly_vars = (:a, :b); poly_deg = 2;
julia> poly_vars = [:a, :b]; poly_deg = 2;

julia> poly_formula = term(:y) ~ term(1) + poly.(poly_vars, poly_deg)
FormulaTerm
Expand Down
4 changes: 2 additions & 2 deletions docs/src/temporal_terms.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Predictors:
lead(x, 2)

julia> modelmatrix(f, df)
5×3 reshape(::Matrix{Union{Missing, Int64}}, 5, 3) with eltype Union{Missing, Int64}:
5×3 Matrix{Union{Missing, Int64}}:
2 missing 6
4 missing 8
6 2 10
Expand Down Expand Up @@ -88,7 +88,7 @@ Predictors:
lead(x, 2)

julia> modelmatrix(f2, df)
5×3 reshape(::Matrix{Union{Missing, Int64}}, 5, 3) with eltype Union{Missing, Int64}:
5×3 Matrix{Union{Missing, Int64}}:
2 missing 6
4 missing 8
6 2 10
Expand Down
2 changes: 1 addition & 1 deletion src/modelframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ keyword arguments are passed to [`apply_schema`](@ref).
[`modelcols`](@ref) pipeline directly

"""
function StatsAPI.modelmatrix(t::Union{AbstractTerm, TupleTerm}, data;
function StatsAPI.modelmatrix(t::TermOrTerms, data;
hints=Dict{Symbol,Any}(), mod::Type{M}=StatisticalModel) where M
Tables.istable(data) ||
throw(ArgumentError("expected data in a Table, got $(typeof(data))"))
Expand Down
44 changes: 25 additions & 19 deletions src/schema.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
terms(t::FormulaTerm) = union(terms(t.lhs), terms(t.rhs))
terms(t::InteractionTerm) = terms(t.terms)
terms(t::FunctionTerm) = mapreduce(terms, union, t.args)
terms(t::AbstractTerm) = [t]
terms(t::AbstractTerm) = AbstractTerm[t]
terms(t::MatrixTerm) = terms(t.terms)
terms(t::TupleTerm) = mapreduce(terms, union, t)
terms(ts::AbstractVector{<:AbstractTerm}) = mapreduce(terms, union, ts, init=AbstractTerm[])

needs_schema(::AbstractTerm) = true
needs_schema(::ConstantTerm) = false
Expand Down Expand Up @@ -82,13 +82,13 @@ julia> ts = [Term(:x), Term(:y)];

julia> schema(ts, d)
StatsModels.Schema with 2 entries:
x => x
y => y
x => x

julia> schema(ts, d, Dict(:x => HelmertCoding()))
StatsModels.Schema with 2 entries:
x => x
y => y
x => x

julia> schema(term(:y), d, Dict(:y => CategoricalTerm))
StatsModels.Schema with 1 entry:
Expand All @@ -101,8 +101,8 @@ same in a container, but when printed alone are different:
```jldoctest 1
julia> sch = schema(ts, d)
StatsModels.Schema with 2 entries:
x => x
y => y
x => x

julia> term(:x)
x(unknown)
Expand All @@ -117,16 +117,13 @@ y(continuous)
schema(data, hints=Dict{Symbol,Any}()) = schema(columntable(data), hints)
schema(dt::D, hints=Dict{Symbol,Any}()) where {D<:ColumnTable} =
schema(Term.(collect(fieldnames(D))), dt, hints)
schema(ts::AbstractVector{<:AbstractTerm}, data, hints::Dict{Symbol}) =
schema(ts, columntable(data), hints)

# handle hints:
schema(ts::AbstractVector{<:AbstractTerm}, dt::ColumnTable,
hints::Dict{Symbol}=Dict{Symbol,Any}()) =
sch = Schema(t=>concrete_term(t, dt, hints) for t in ts)

schema(f::TermOrTerms, data, hints::Dict{Symbol}) =
schema(filter(needs_schema, terms(f)), data, hints)
schema(f, columntable(data), hints)

# handle hints:
schema(f::TermOrTerms, dt::ColumnTable, hints::Dict{Symbol}=Dict{Symbol,Any}()) =
Schema(t => concrete_term(t, dt, hints) for t in filter(needs_schema, terms(f)))

schema(f::TermOrTerms, data) = schema(f, data, Dict{Symbol,Any}())

Expand Down Expand Up @@ -237,7 +234,16 @@ in _most_ cases, but cause method ambiguity in some.
"""
apply_schema(t, schema) = apply_schema(t, schema, Nothing)
apply_schema(t, schema, Mod::Type) = t
apply_schema(terms::TupleTerm, schema, Mod::Type) = reduce(+, apply_schema.(terms, Ref(schema), Mod))
# sequential (left-to-right) application over a vector of terms, combined with
# `+` so that duplicates are dropped and nested vectors are flattened; always
# returns a Vector{AbstractTerm}
function apply_schema(terms::AbstractVector{<:AbstractTerm}, schema, Mod::Type)
out = AbstractTerm[]
for t in terms
out = out + apply_schema(t, schema, Mod)
end
return out
end

apply_schema(t::Term, schema::Schema, Mod::Type) = schema[t]
apply_schema(ft::FormulaTerm, schema::Schema, Mod::Type) =
Expand All @@ -250,7 +256,7 @@ apply_schema(it::InteractionTerm, schema::Schema, Mod::Type) =
apply_schema(t::Union{ContinuousTerm, CategoricalTerm}, schema::Schema, Mod::Type) =
get(schema, term(t.sym), t)
apply_schema(t::MatrixTerm, sch::Schema, Mod::Type) =
MatrixTerm(apply_schema.(t.terms, Ref(sch), Mod))
MatrixTerm(apply_schema(t.terms, sch, Mod))

# TODO: special case this for <:RegressionModel ?
function apply_schema(t::ConstantTerm, schema::Schema, Mod::Type)
Expand Down Expand Up @@ -390,7 +396,7 @@ has_schema(t::ConstantTerm) = false
has_schema(t::Term) = false
has_schema(t::Union{ContinuousTerm,CategoricalTerm}) = true
has_schema(t::InteractionTerm) = all(has_schema(tt) for tt in t.terms)
has_schema(t::TupleTerm) = all(has_schema(tt) for tt in t)
has_schema(t::AbstractVector{<:AbstractTerm}) = all(has_schema(tt) for tt in t)
has_schema(t::MatrixTerm) = has_schema(t.terms)
has_schema(t::FormulaTerm) = has_schema(t.lhs) && has_schema(t.rhs)
# FunctionTerms may always be transformed by apply_schema
Expand Down Expand Up @@ -503,8 +509,8 @@ end
drop_term(from, to) = symequal(from, to) ? ConstantTerm(1) : from
drop_term(from::FormulaTerm, to) = FormulaTerm(from.lhs, drop_term(from.rhs, to))
drop_term(from::MatrixTerm, to) = MatrixTerm(drop_term(from.terms, to))
drop_term(from::TupleTerm, to) =
tuple((t for t = from if !symequal(t, to))...)
drop_term(from::AbstractVector{<:AbstractTerm}, to) =
AbstractTerm[t for t in from if !symequal(t, to)]
function drop_term(from::InteractionTerm, t)
terms = drop_term(from.terms, t)
length(terms) > 1 ? InteractionTerm(terms) : terms[1]
Expand Down Expand Up @@ -537,7 +543,7 @@ The data variables that this term refers to.
termvars(::AbstractTerm) = Symbol[]
termvars(t::Union{Term, CategoricalTerm, ContinuousTerm}) = [t.sym]
termvars(t::InteractionTerm) = mapreduce(termvars, union, t.terms)
termvars(t::TupleTerm) = mapreduce(termvars, union, t, init=Symbol[])
termvars(ts::AbstractVector{<:AbstractTerm}) = mapreduce(termvars, union, ts, init=Symbol[])
termvars(t::MatrixTerm) = termvars(t.terms)
termvars(t::FormulaTerm) = union(termvars(t.lhs), termvars(t.rhs))
termvars(t::FunctionTerm) = mapreduce(termvars, union, t.args, init=Symbol[])
Expand Down
2 changes: 1 addition & 1 deletion src/temporal_terms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct LeadLagTerm{T<:AbstractTerm, F<:Union{typeof(lead), typeof(lag)}} <: Abst
nsteps::Int
end

terms(t::LeadLagTerm) = (t.term, )
terms(t::LeadLagTerm) = AbstractTerm[t.term]

function apply_schema(t::FunctionTerm{F}, sch::Schema, ctx::Type) where F<:Union{typeof(lead), typeof(lag)}
opname = string(nameof(F.instance))
Expand Down
Loading
Loading