File: p_value_kenward.R

package info (click to toggle)
r-cran-parameters 0.24.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,852 kB
  • sloc: sh: 16; makefile: 2
file content (144 lines) | stat: -rw-r--r-- 4,948 bytes parent folder | download
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
#' @title Kenward-Roger approximation for SEs, CIs and p-values
#' @name p_value_kenward
#'
#' @description An approximate F-test based on the Kenward-Roger (1997) approach.
#'
#' @param model A statistical model.
#' @param dof Degrees of Freedom.
#' @inheritParams ci.default
#'
#' @details Inferential statistics (like p-values, confidence intervals and
#' standard errors) may be biased in mixed models when the number of clusters
#' is small (even if the sample size of level-1 units is high). In such cases
#' it is recommended to approximate a more accurate number of degrees of freedom
#' for such inferential statistics. Unlike simpler approximation heuristics
#' like the "m-l-1" rule (`dof_ml1`), the Kenward-Roger approximation is
#' also applicable in more complex multilevel designs, e.g. with cross-classified
#' clusters. However, the "m-l-1" heuristic also applies to generalized
#' mixed models, while approaches like Kenward-Roger or Satterthwaite are limited
#' to linear mixed models only.
#'
#' @seealso `dof_kenward()` and `se_kenward()` are small helper-functions
#' to calculate approximated degrees of freedom and standard errors for model
#' parameters, based on the Kenward-Roger (1997) approach.
#'
#' [`dof_satterthwaite()`] and [`dof_ml1()`] approximate degrees of freedom
#' based on Satterthwaite's method or the "m-l-1" rule.
#'
#' @examples
#' \donttest{
#' if (require("lme4", quietly = TRUE)) {
#'   model <- lmer(Petal.Length ~ Sepal.Length + (1 | Species), data = iris)
#'   p_value_kenward(model)
#' }
#' }
#' @return A data frame.
#' @references Kenward, M. G., & Roger, J. H. (1997). Small sample inference for
#'   fixed effects from restricted maximum likelihood. Biometrics, 983-997.
#' @export
p_value_kenward <- function(model, dof = NULL) {
  UseMethod("p_value_kenward")
}


#' @export
p_value_kenward.lmerMod <- function(model, dof = NULL) {
  if (is.null(dof)) {
    dof <- dof_kenward(model)
  }
  .p_value_dof(model, dof, method = "kenward")
}


# helper ------------------------------

.p_value_dof <- function(model,
                         dof,
                         method = NULL,
                         statistic = NULL,
                         se = NULL,
                         component = c("all", "conditional", "zi", "zero_inflated", "dispersion", "precision", "scale", "smooth_terms", "full", "marginal"),
                         effects = c("fixed", "random", "all"),
                         verbose = TRUE,
                         vcov = NULL,
                         vcov_args = NULL,
                         ...) {
  component <- match.arg(component)
  effects <- match.arg(effects)

  if (is.null(.check_component(model, component, verbose = verbose))) {
    return(NULL)
  }

  params <- insight::get_parameters(model, component = component)

  # check if all estimates are non-NA
  params <- .check_rank_deficiency(model, params, verbose = FALSE)

  if (is.null(statistic)) {
    statistic <- insight::get_statistic(model, component = component)
    params <- merge(params, statistic, sort = FALSE)
    statistic <- params$Statistic
  }

  # different SE for kenward and robust
  if (identical(method, "kenward") || identical(method, "kr")) {
    if (is.null(se)) {
      se <- se_kenward(model)$SE
    }
  } else if (!is.null(vcov)) {
    se <- standard_error(model,
      vcov = vcov,
      vcov_args = vcov_args,
      component = component,
      ...
    )$SE
  }

  # overwrite statistic, based on robust or kenward standard errors
  if (identical(method, "kenward") || identical(method, "kr") || !is.null(vcov)) {
    estimate <- if ("Coefficient" %in% colnames(params)) {
      params$Coefficient
    } else {
      params$Estimate
    }
    statistic <- estimate / se
  }

  p <- 2 * stats::pt(abs(statistic), df = dof, lower.tail = FALSE)
  out <- .data_frame(
    Parameter = params$Parameter,
    p = unname(p)
  )

  if ("Component" %in% names(params)) out$Component <- params$Component
  if ("Effects" %in% names(params) && effects != "fixed") out$Effects <- params$Effects
  if ("Response" %in% names(params)) out$Response <- params$Response

  out
}


.p_value_dof_kr <- function(model, params, dof) {
  if ("SE" %in% colnames(params) && "SE" %in% colnames(dof)) {
    params$SE <- NULL
  }
  params <- merge(params, dof, by = "Parameter")
  p <- 2 * stats::pt(abs(params$Estimate / params$SE), df = params$df_error, lower.tail = FALSE)

  .data_frame(
    Parameter = params$Parameter,
    p = unname(p)
  )
}


# helper -------------------------

.check_REML_fit <- function(model) {
  insight::check_if_installed("lme4")

  if (!(lme4::getME(model, "is_REML"))) {
    insight::format_warning("Model was not fitted by REML. Re-fitting model now, but p-values, df, etc. still might be unreliable.")
  }
}