2

I would like to manipulate a named vector within a list, and do that for a large number of similarly named lists. Specifically, my lists are results from glm, and I want to change the names of the coefficients list element.

Here's a toy example:

model_1 <- glm(Petal.Width ~ Sepal.Length*Sepal.Width, data = iris)
model_2 <- glm(Petal.Length ~ Sepal.Length*Sepal.Width, data = iris)

The desired manipulation for one list:

names(model_1$coefficients) <- c("Constant", "Length", "Width", "Length * Width")

Now trying to do this for both lists:

for (i in 1:2) {
  list_name <- paste("model", i, sep = ""),
  names(list_name$coefficients) <- c("Constant", "Length", "Width", "Length * Width")
 }

But of course, this does not work because R tries to evaluate a list called "list_name". How could I make it evaluate the list named as the variable "list_name"?

broti
  • 1,338
  • 8
  • 29

3 Answers3

3

While both previous answers include valid solutions to your specific problem, I would strongly suggest to use a list as a container for all your models, as this would make the overall handling of them much easier.

models <- list()
models$m_1 <- glm(Petal.Width ~ Sepal.Length*Sepal.Width, data = iris)
models$m_2 <- glm(Petal.Length ~ Sepal.Length*Sepal.Width, data = iris)

coefNames <- c("Constant", "Length", "Width", "Length * Width")

models <- lapply(models, function(x) {
  names(x$coefficients) <- coefNames
  x })

Or in tidyverse:

models <- map(models, ~  { 
    .$coefficients <- set_names(.$coefficients, coefNames)
. })

Or, the simplest solution, with a for loop:

for(i in 1:length(models)) {
    names(models[[i]]$coefficients) <- coefNames
}

Or, say, you have a selection of models:

selmods <- paste0("m_", 1:2)
for(i in selmods) {
   names(models[[i]]$coefficients) <- coefNames
}
broti
  • 1,338
  • 8
  • 29
January
  • 16,320
  • 6
  • 52
  • 74
  • While I like to simplicity through such a "meta list", this solution does not allow me to apply the operation to specific models with similar names (see the loop in the original question). How would you adapt your approach? – broti Jul 15 '19 at 13:48
  • Why wouldn't it? You can do *more* on lists than what you can do on environments in this respect. Whenever you access a list element by index, you can also access it by name (if it exists). You can use `[[1]]` as well as `[["mod_1"]]`. – January Jul 15 '19 at 13:54
  • Also, "meta lists", seriously? :-) Treat lists more like a directory structure: in "results", I have directories "model 1", "model 2" etc..., and in each of them I have a folder "Coefficients" (etc). Environment is a special kind of directory, much like a desktop, in which everything is visible. Which is a reason either not to use it or not to make it messy. – January Jul 15 '19 at 13:57
  • Excuse my inexcusably colloquial language. Thanks for the addition - is there also a way to something similar with `lapply`? – broti Jul 15 '19 at 17:08
  • :-) No, I was referring to the fact that something so everyday appears so strange to someone else. Of course you can use names in `lapply`, `sapply`, `map` (from `dplyr`) and all others! `lapply(selmods, function(m) do_something_or_other_with(models[[m]]))`. If you can use an index, you can use a name as well. – January Jul 15 '19 at 19:01
2

Use a combination of get and assign:

set_coef_names <- function(x) {
  names(x$coefficients) <- c("Constant", "Length", "Width", "Length * Width")
  return(x)
}

for (i in 1:2) {
  assign(paste("model_", i, sep = ""), set_coef_names(get(paste("model_", i, sep = ""))))
}
eastclintw00d
  • 2,250
  • 1
  • 9
  • 18
  • 1
    `fortunes::fortune(236)`: The only people who should use the assign function are those who fully understand why you should never use the assign function. -- Greg Snow, R-help (July 2009) – Uwe Jul 14 '19 at 09:21
2

This can also be solved without assign() *:

lapply(
  mget(paste0("model_", 1:2)), 
  function(x) {
    names(x$coefficients) <- c("Constant", "Length", "Width", "Length * Width")
    x
  }
)
$model_1

Call:  glm(formula = Petal.Width ~ Sepal.Length * Sepal.Width, data = iris)

Coefficients:
      Constant          Length           Width  Length * Width  
        3.9532         -0.2490         -2.2488          0.3129  

Degrees of Freedom: 149 Total (i.e. Null);  146 Residual
Null Deviance:        86.57 
Residual Deviance: 20.9   AIC: 140.1

$model_2

Call:  glm(formula = Petal.Length ~ Sepal.Length * Sepal.Width, data = iris)

Coefficients:
      Constant          Length           Width  Length * Width  
        6.3910          0.2042         -4.1994          0.5057  

Degrees of Freedom: 149 Total (i.e. Null);  146 Residual
Null Deviance:        464.3 
Residual Deviance: 57.91  AIC: 292.9

mget() searches the environment for objects by name and returns a list of the objects. lapply() applies the function on each of the list elements and returns a list, again.


* There are many voices which recommend to avoid assign(), e.g.,

  • R FAQ
  • Why is using assign bad?
  • fortunes::fortune(236): "The only people who should use the assign function are those who fully understand why you should never use the assign function." -- Greg Snow, R-help (July 2009)
Community
  • 1
  • 1
Uwe
  • 41,420
  • 11
  • 90
  • 134
  • Thanks @Uwe, but that does not actually change `model_1` and `model_2` in the global environment. How do I best do that? – broti Jul 15 '19 at 13:32