diff --git a/NEWS.md b/NEWS.md index 0a63d124f..c46f64194 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +MixedModels v5.2.0 Release Notes +============================== +- The use of the `wts` keyword argument has been deprecated in favor of the keyword argument `weights`, in line with the deprecation in GLM.jl v1.9.1. The usage (and subsequent interpretation) remains otherwise unchanged. [#873] + MixedModels v5.1.0 Release Notes ============================== - Nesting checks for the likelihoodratio test have been slightly tweaked to be more robust, at the cost of being slightly slower. In particular, the comparison of models with pre-centered variables with those with variables centered via StandardizedPredictors.jl was previously incorrectly rejected as non-nested, but should be correctly accepted as nested now. Additionally, some further logging messages are emitted when a nesting check fails. [#867] @@ -710,3 +714,4 @@ Package dependencies [#864]: https://github.com/JuliaStats/MixedModels.jl/issues/864 [#865]: https://github.com/JuliaStats/MixedModels.jl/issues/865 [#867]: https://github.com/JuliaStats/MixedModels.jl/issues/867 +[#873]: https://github.com/JuliaStats/MixedModels.jl/issues/873 diff --git a/Project.toml b/Project.toml index 96d223b6c..6f0a883bb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MixedModels" uuid = "ff71e718-51f3-5ec2-a782-8ffcbfa3c316" author = ["Phillip Alday ", "Douglas Bates "] -version = "5.1.0" +version = "5.2.0" [deps] Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45" @@ -73,7 +73,7 @@ StableRNGs = "0.1, 1" StaticArrays = "0.11, 0.12, 1" Statistics = "1" StatsAPI = "1.5" -StatsBase = "0.31, 0.32, 0.33, 0.34" +StatsBase = "0.34" StatsFuns = "0.8, 0.9, 1" StatsModels = "0.7" StructTypes = "1" diff --git a/docs/Project.toml b/docs/Project.toml index c396b21bc..9ed02bbdb 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -26,13 +26,14 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d" [compat] +AlgebraOfGraphics = "0.12" BenchmarkTools = "1" DataFrames = "1" Documenter = "1.3" FreqTables = "0.4" Gadfly = "1" StatsAPI = "1.5" -StatsBase = "0.33" +StatsBase = "0.34" [sources.MixedModels] path = ".." diff --git a/docs/src/ecosystem.md b/docs/src/ecosystem.md index 53d0ba028..82aa5fe45 100644 --- a/docs/src/ecosystem.md +++ b/docs/src/ecosystem.md @@ -8,7 +8,7 @@ Several packages extend the functionality of MixedModels.jl, both in ways specif ```@example Ecosystem using MixedModels -progress = false +progress = isinteractive() ``` ```@example Ecosystem @@ -182,16 +182,16 @@ Effects are particularly nice for visualizing the model fit and its predictions. using AlgebraOfGraphics # like ggplot2, but an algebra instead of a grammar using CairoMakie -plt1 = data(eff_logit) * - mapping(:age, :use; color=:anych) * - (visual(Lines) + mapping(; lower=:lower, upper=:upper) * visual(LinesFill)) +plt1 = data(eff_logit) * mapping(:age; color=:anych) * + (mapping(:use) * visual(Lines) + + mapping(:lower, :upper) * visual(Band; alpha=0.3)) draw(plt1) ``` ```@example Ecosystem -plt2 = data(eff_prob) * - mapping(:age, :use; color=:anych => "children") * - (visual(Lines) + mapping(; lower=:lower, upper=:upper) * visual(LinesFill)) +plt2 = data(eff_prob) * mapping(:age; color=:anych) * + (mapping(:use) * visual(Lines) + + mapping(:lower, :upper) * visual(Band; alpha=0.3)) draw(plt2) ``` diff --git a/src/MixedModels.jl b/src/MixedModels.jl index 8f77cac96..61d184c84 100644 --- a/src/MixedModels.jl +++ b/src/MixedModels.jl @@ -37,7 +37,7 @@ using StatsAPI: dof, dof_residual, fit, fit!, fitted, isfitted, islinear, levera using StatsAPI: loglikelihood, meanresponse, modelmatrix, nobs, pvalue, predict, r2, residuals using StatsAPI: response, responsename, stderror, vcov, weights -using StatsBase: StatsBase, CoefTable, model_response, summarystats +using StatsBase: StatsBase, CoefTable, model_response, summarystats, FrequencyWeights using StatsFuns: chisqccdf, log2π, normccdf using StatsModels: StatsModels, AbstractContrasts, AbstractTerm, CategoricalTerm using StatsModels: ConstantTerm, DummyCoding, EffectsCoding, FormulaTerm, FunctionTerm diff --git a/src/generalizedlinearmixedmodel.jl b/src/generalizedlinearmixedmodel.jl index 60b113e9f..1df500500 100644 --- a/src/generalizedlinearmixedmodel.jl +++ b/src/generalizedlinearmixedmodel.jl @@ -177,14 +177,17 @@ function StatsAPI.fit( tbl::Tables.ColumnTable, d::Distribution, l::Link=canonicallink(d); - wts=[], + weights=[], + wts=nothing, contrasts=Dict{Symbol,Any}(), offset=[], amalgamate=true, kwargs..., ) return fit!( - GeneralizedLinearMixedModel(f, tbl, d, l; wts, offset, contrasts, amalgamate); + GeneralizedLinearMixedModel( + f, tbl, d, l; weights, wts, offset, contrasts, amalgamate + ); kwargs..., ) end @@ -368,12 +371,21 @@ function GeneralizedLinearMixedModel( tbl::Tables.ColumnTable, d::Distribution, l::Link=canonicallink(d); - wts=[], + weights=[], + wts=nothing, offset=[], contrasts=Dict{Symbol,Any}(), amalgamate=true, ) - if isa(d, Binomial) && isempty(wts) + if wts !== nothing + Base.depwarn( + "`wts` keyword argument is deprecated, use `weights` instead", + :GeneralizedLinearMixedModel, + ) + weights = wts + end + + if isa(d, Binomial) && isempty(weights) d = Bernoulli() end (isa(d, Normal) && isa(l, IdentityLink)) && throw( @@ -386,13 +398,13 @@ function GeneralizedLinearMixedModel( the authors gain a better understanding of those cases.""" end - LMM = LinearMixedModel(f, tbl; contrasts, wts, amalgamate) + LMM = LinearMixedModel(f, tbl; contrasts, weights, amalgamate) y = copy(LMM.y) constresponse = all(==(first(y)), y) # the sqrtwts field must be the correct length and type but we don't know those # until after the model is constructed if wt is empty. Because a LinearMixedModel # type is immutable, another one must be created. - if isempty(wts) + if isempty(weights) LMM = LinearMixedModel( LMM.formula, LMM.reterms, @@ -421,10 +433,12 @@ function GeneralizedLinearMixedModel( # TODO: construct GLM by hand so that we skip collinearity checks # TODO: extend this so that we never fit a GLM when initializing from LMM dofit = size(X, 2) != 0 # GLM.jl kwarg + wtkwarg = pkgversion(GLM) >= v"1.9.1" ? :weights : :wts + weights = convert(Vector{T}, weights) gl = glm(X, y, d, l; - wts=convert(Vector{T}, wts), + wtkwarg => pkgversion(GLM) >= v"1.9.1" ? FrequencyWeights(weights) : weights, dofit, - offset=convert(Vector{T}, offset)) + :offset => convert(Vector{T}, offset)) β = dofit ? coef(gl) : T[] u = [fill(zero(eltype(y)), vsize(t), nlevs(t)) for t in LMM.reterms] # vv is a template vector used to initialize fields for AGQ @@ -441,7 +455,7 @@ function GeneralizedLinearMixedModel( zero.(u), gl.rr, similar(y), - oftype(y, wts), + weights, similar(vv), similar(vv), similar(vv), diff --git a/src/linearmixedmodel.jl b/src/linearmixedmodel.jl index de2b3bcc6..b2a17f8e3 100644 --- a/src/linearmixedmodel.jl +++ b/src/linearmixedmodel.jl @@ -40,12 +40,8 @@ struct LinearMixedModel{T<:AbstractFloat} <: MixedModel{T} optsum::OptSummary{T} end -function LinearMixedModel( - f::FormulaTerm, tbl; contrasts=Dict{Symbol,Any}(), wts=[], σ=nothing, amalgamate=true -) - return LinearMixedModel( - f::FormulaTerm, Tables.columntable(tbl); contrasts, wts, σ, amalgamate - ) +function LinearMixedModel(f::FormulaTerm, tbl; kwargs...) + return LinearMixedModel(f::FormulaTerm, Tables.columntable(tbl); kwargs...) end const _MISSING_RE_ERROR = ArgumentError( @@ -53,7 +49,8 @@ const _MISSING_RE_ERROR = ArgumentError( ) function LinearMixedModel( - f::FormulaTerm, tbl::Tables.ColumnTable; contrasts=Dict{Symbol,Any}(), wts=[], + f::FormulaTerm, tbl::Tables.ColumnTable; contrasts=Dict{Symbol,Any}(), wts=nothing, + weights=[], σ=nothing, amalgamate=true, ) fvars = StatsModels.termvars(f) @@ -65,6 +62,13 @@ function LinearMixedModel( ), ) + if wts !== nothing + Base.depwarn( + "`wts` keyword argument is deprecated, use `weights` instead", :LinearMixedModel + ) + weights = wts + end + # TODO: perform missing_omit() after apply_schema() when improved # missing support is in a StatsModels release tbl, _ = StatsModels.missing_omit(tbl, f) @@ -76,11 +80,11 @@ function LinearMixedModel( y, Xs = modelcols(form, tbl) - return LinearMixedModel(y, Xs, form, wts, σ, amalgamate) + return LinearMixedModel(y, Xs, form, weights, σ, amalgamate) end """ - LinearMixedModel(y, Xs, form, wts=[], σ=nothing, amalgamate=true) + LinearMixedModel(y, Xs, form, weights=[], σ=nothing, amalgamate=true) Private constructor for a LinearMixedModel. @@ -96,7 +100,7 @@ function LinearMixedModel( y::AbstractArray, Xs::Tuple, # can't be more specific here without stressing the compiler form::FormulaTerm, - wts=[], + weights=[], σ=nothing, amalgamate=true, ) @@ -132,12 +136,12 @@ function LinearMixedModel( end isempty(reterms) && throw(_MISSING_RE_ERROR) return LinearMixedModel( - convert(Array{T}, y), only(feterms), reterms, form, wts, σ, amalgamate + convert(Array{T}, y), only(feterms), reterms, form, weights, σ, amalgamate ) end """ - LinearMixedModel(y, feterm, reterms, form, wts=[], σ=nothing; amalgamate=true) + LinearMixedModel(y, feterm, reterms, form, weights=[], σ=nothing; amalgamate=true) Private constructor for a `LinearMixedModel` given already assembled fixed and random effects. @@ -155,7 +159,7 @@ function LinearMixedModel( feterm::FeTerm{T}, reterms::AbstractVector{<:AbstractReMat{T}}, form::FormulaTerm, - wts=[], + weights=[], σ=nothing, amalgamate=true, ) where {T} @@ -168,7 +172,7 @@ function LinearMixedModel( sort!(reterms; by=nranef, rev=true) Xy = FeMat(feterm, vec(y)) - sqrtwts = map!(sqrt, Vector{T}(undef, length(wts)), wts) + sqrtwts = map!(sqrt, Vector{T}(undef, length(weights)), weights) reweight!.(reterms, Ref(sqrtwts)) reweight!(Xy, sqrtwts) A, L = createAL(reterms, Xy) @@ -206,12 +210,13 @@ end function StatsAPI.fit(::Type{LinearMixedModel}, f::FormulaTerm, tbl::Tables.ColumnTable; - wts=[], + weights=[], + wts=nothing, contrasts=Dict{Symbol,Any}(), σ=nothing, amalgamate=true, kwargs...) - lmod = LinearMixedModel(f, tbl; contrasts, wts, σ, amalgamate) + lmod = LinearMixedModel(f, tbl; contrasts, weights, wts, σ, amalgamate) return fit!(lmod; kwargs...) end