File: predictorEffects.R

package info (click to toggle)
effects 4.2.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,852 kB
  • sloc: makefile: 4
file content (207 lines) | stat: -rw-r--r-- 8,393 bytes parent folder | download | duplicates (3)
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
# 2017-08-14 fixed bug in plot.predictoreff on passing 'multiline' to lines list
# 2017-08-30 for compatibility with other effect plots, default
#            is now multiline=FALSE
# 2017-11-09 fixed bug in setting the class for multinom models, and possibly others
# 2017-11-17 added methods for clm, clm2, clmm in the file effectsclmm.R
# 2017-12-08 modified predictorEffect.default and predictorEffects.default for compatibility to Effect.default
# 2018-01-09 fixed bug in predictorEffects.default with log() in a formula.
# 2018-01-24 fixed bug with minus sign in a formula predictorEffects.default
# 2018-05-14 predictorEffect.default() calls Effect() with x.var=1
# 2018-06-07 predictorEffects now works with offsets.
# 2018-08-09 removed explicit 'xlevels' argument from predictorEffects, so the argument is correctly passed with ...
# 2018-10-19: changed class of predictorefflist to c("predictorefflist", "efflist", "list")
# 2018-11-19: added xlevels argument with default 5 to be applied to conditioning predictors and
#             focal.levels argument with default 50 to be applied to focal predictor. J. Fox
# 2019-04-13: changed behavior of xlevels default to match Effect.lm() when residuals=TRUE. J. Fox
# 2020-05-29: use find_formula in the 'insight' package to find formulas.  S. Weisberg
# 2020-06-23: modified predictorEffect.default to get formula using effSources. S Weisberg

# removed xlevels argument 8/9/18
predictorEffect <- function(predictor, mod, focal.levels=50, xlevels=5, ...){  
  UseMethod("predictorEffect", mod)
}

# removed xlevels argument 8/9/18
predictorEffect.svyglm <- function(predictor, mod, focal.levels=50, xlevels=5, ...){
  mod$call <- list(mod$call, data=mod$data)
  NextMethod(object=mod, ...)
}

#simplified 12/10/17
# removed xlevels argument 8/9/18
predictorEffect.default <- function(predictor, mod, focal.levels=50, xlevels=5, ..., sources){
  dots <- list(...)
  which.residuals <- which(!is.na(sapply(names(dots), 
                                         function(x) pmatch(x, c("residuals", "partial.residuals")))))
  if (length(which.residuals) != 0){
    if (isTRUE(dots[[which.residuals]]) && missing(xlevels)) xlevels <- list()
  }
  sources <- if(missing(sources)) effSources(mod) else sources
  form <- if(is.null(sources$formula)) 
    find_formula(mod)$conditional else sources$formula
  all.vars <- all.vars(parse(text=form))
# all.vars <- find_terms(mod, flatten=TRUE)
  # find the right effect to use
  terms <- attr(terms(form), "term.labels")
  # get the predictor names:
  predictors <- all.vars(parse(text=terms))
# predictors <- find_terms(m2)$conditional
  sel <- which(predictors == predictor)
  if(length(sel) != 1) stop("First argument must be the quoted name of one predictor in the formula")
  if (is.numeric(xlevels)){
    if (length(xlevels) > 1 || round(xlevels != xlevels)) stop("xlevels must be a single whole number or a list")
    xlevs <- list()
    for (pred in predictors[-sel]){
      xlevs[[pred]] <- xlevels
    }
    xlevels <- xlevs
  }
  xlevels[[predictor]] <- focal.levels
  # create correspondence table
  decode <- function(name) all.vars(parse(text=unlist(strsplit(name, ":"))))
  tab <- rep(FALSE, length(terms))
  for(j in 1:length(terms)){if(predictor %in% decode(terms[j])) tab[j] <- TRUE}
  ans <- unlist(strsplit(paste(terms[tab], collapse=":"), ":"))
  ans <- unique(all.vars(parse(text=ans)))
  ans <- unique(c(predictor, ans)) # guarantees focal predictor is first
  args <- names(list(...))
  result <- if ("x.var" %in% args) Effect(ans, mod, xlevels=xlevels, ...) else Effect(ans, mod, x.var=1, xlevels=xlevels, ...)
  class(result) <- c("predictoreff", class(result))
  result
}

predictorEffects <- function(mod, predictors, focal.levels=50, xlevels=5, ...){
  UseMethod("predictorEffects", mod)
}


# rewritten, simplified, 12/08/17, bug in formulas fixed 01/24/2018
predictorEffects.default <- function(mod, predictors = ~ ., focal.levels=50, xlevels=5, ..., sources) {
  dots <- list(...)
  which.residuals <- which(!is.na(sapply(names(dots), 
                           function(x) pmatch(x, c("residuals", "partial.residuals")))))
  if (length(which.residuals) != 0){
    if (isTRUE(dots[[which.residuals]]) && missing(xlevels)) xlevels <- list()
  }
# The next function removes offset(s) from a formula, used for mform and cform
  no.offset <- function(x, preserve = NULL) {
    k <- 0
    proc <- function(x) {
      if (length(x) == 1) return(x)
      if (x[[1]] == as.name("offset") && !((k<<-k+1) %in% preserve)) return(x[[1]])
      replace(x, -1, lapply(x[-1], proc))
    }
    update(proc(x), . ~ . - offset)}
  sources <- if(missing(sources)) effSources(mod) else sources
  form <- if(is.null(sources$formula)) {find_formula(mod)$conditional} else sources$formula
  mform <- no.offset(form) # replacement for next line
#  mform <- no.offset(Effect.default(NULL, mod))  # returns the fixed-effect formula for any method
  cform <- if(is.character(predictors)) 
    as.formula(paste("~", paste(predictors, collapse="+"))) else
      predictors
  cform <- update(as.formula(paste(". ~", 
                paste(all.vars(formula(mform)[[3]]), collapse="+"))), 
                cform)
  cform <- no.offset(cform)
  mvars <- all.vars(mform[[3]])
  cvars <- all.vars(cform[[3]])
  if (is.list(focal.levels)){
    for(cvar in cvars){
      if (!is.null(focal.levels[[cvar]])) next
      focal.levels[[cvar]] <- 50
    }
  } else{
    if (!is.vector(focal.levels) || !is.numeric(focal.levels) || length(focal.levels) > 1 || round(focal.levels) != focal.levels)
      stop("focal.levels must be a length 1 positive\nwhole-number, numeric vector or a list")
  }
  if (length(xlevels) > 0){
    if (is.list(xlevels)){
      for(mvar in mvars){
        if (!is.null(xlevels[[mvar]])) next
        xlevels[[mvar]] <- 5
      }
    } else{
      if (!is.vector(xlevels) || !is.numeric(xlevels) || length(xlevels) > 1 || round(xlevels) != xlevels)
        stop("xlevels must be a length 1 positive\nwhole-number, numeric vector or a list")
    }
  }
# check that 'cvars' is a subset of 'mvars'. If so apply predictorEffect
  if(!all(cvars %in% mvars)){
    stop("argument 'predictors' not a subset of the predictors in the formula") 
  } else {
    result <- list()
    for(p in cvars){
      flevs <- if (is.numeric(focal.levels)) focal.levels else focal.levels[[p]]
      result[[p]] <- predictorEffect(p, mod, focal.levels=flevs, xlevels=xlevels, ...,
                                     sources=sources)
    }
  }
  class(result) <- c("predictorefflist", "efflist", "list")
  result
}

# plot methods

plot.predictoreff <- function(x, x.var,
                              main = paste(names(x$variables)[1], "predictor effect plot"), ...){
  if(missing(x.var)) x.var <- names(x$variables)[1]
  NextMethod(x, x.var=x.var, main=main, ...)
}


plot.predictorefflist <- function(x, selection, rows, cols, ask=FALSE, graphics=TRUE,
                                  lattice, ...){
  # Next line added 8/23/17 along with lattice, also lattice arg above
  lattice <- if(missing(lattice)) list() else lattice
  if (!missing(selection)){
    if (is.character(selection)) selection <- gsub(" ", "", selection)
    return(plot(x[[selection]], ...))
  }
  effects <- gsub(":", "*", names(x))
  if (ask){
    repeat {
      selection <- menu(effects, graphics=graphics, title="Select Term to Plot")
      if (selection == 0) break
      else print(plot(x[[selection]], ...))
    }
  }
  else {
    neffects <- length(x)
    mfrow <- mfrow(neffects)
    if (missing(rows) || missing(cols)){
      rows <- mfrow[1]
      cols <- mfrow[2]
    }
    for (i in 1:rows) {
      for (j in 1:cols){
        if ((i-1)*cols + j > neffects) break
        more <- !((i-1)*cols + j == neffects)
        lattice[["array"]] <- list(row=i, col=j, nrow=rows, ncol=cols, more=more)
        print(plot(x[[(i-1)*cols + j]], lattice=lattice,
                   ...))
      }
    }
  }
}


# print and summary methods

print.predictorefflist <- function(x, ...){
  for (eff in x){
    print(eff, ...)
  }
  invisible(x)
}

print.predictoreff <- function(x, ...){
  cat("\n", names(x$variables)[1], "predictor effect\n")
  NextMethod()
}

summary.predictorefflist <- function(object, ...){
  for (eff in object){
    cat("\n", names(eff$variables)[1], "predictor effect\n")
    print(summary(eff, ...))
  }
}