Перебор отдельных списков в R

#r #list #loops

#r #Список #циклы

Вопрос:

У меня много переменных в R, все типа list

 a100 = list()
a200 = list()
# ...
p700 = list()
  

Каждая переменная представляет собой сложную структуру данных:

 a200$time$data # returns 1000 x 1000 matrix
  

Теперь я хочу применить код к каждой переменной по очереди. Однако, поскольку R не поддерживает передачу по ссылке, я не уверен, что делать.

Одна из моих идей заключалась в том, чтобы создать большой список из всех этих списков, т.е.,

 biglist = list()
biglist[[1]] = a100
...
  

И тогда я мог бы перебрать biglist:

 for (i in 1:length(biglist)){
    biglist[[i]]$newstuff = "profit"
    # more code here
}
  

И, наконец, после цикла вернитесь назад, чтобы существующий код (использующий имена переменных) все еще работал:

 a100 = biglist[[1]]
# ...
  

Вопрос в том, есть ли лучший способ перебора набора именованных списков? У меня такое чувство, что я делаю что-то ужасно неправильно. Есть ли что-нибудь попроще, например:

 # FAKE, Idealized code:
foreach x in (a100, a200, ....){
    x$newstuff = "profit"
}

a100$newstuff # "profit"
  

Ответ №1:

Для параллельного обхода списков вы можете использовать mapply, который будет принимать параллельные списки, а затем проходить по ним с шагом блокировки. Кроме того, на функциональном языке вы должны создавать нужный объект, а не изменять структуру данных в вызове функции.

Вам следует использовать семейство функций sapply, apply, lapply, ….

джим

Комментарии:

1. Спасибо за мысли, Джим. Как я могу использовать lapply и при этом изменять исходные списки? Например, если бы я это сделал lapply(biglist, function(x){x$newstuff = "profit"; x}) , оригинал biglist не был бы изменен, не так ли? Каждый x будет просто выводиться на экран, нигде не сохраняясь?

2. Джим — Поразмыслив об этом еще немного, я решил, что ваш совет выдавать объект, а не изменять его, является верным. Гораздо лучше структурировать мой код как a100 = computeStuff(a100) , чем все те сложности, о которых я думал. Спасибо за этот совет.

Ответ №2:

джимми совершенно прав. lapply и sapply специально разработаны для работы со списками. Таким образом, они будут работать и с вашим biglist. Однако вы не должны забывать возвращать объект во вложенной функции: пример :

 X <- list(A=list(A1=1:2,A2=3:4),B=list(B1=5:6,B2=7:8))

lapply(X,function(i){
    i$newstuff = "profit"
    return(i)
})
  

Теперь, как вы сказали, R передается по значению, поэтому у вас есть несколько копий данных, перемещающихся по всему. Если вы работаете с действительно большими списками, возможно, вам захочется попробовать снизить потребление памяти, работая с каждой переменной отдельно, используя assign и get . Следующее считается неправильным кодированием, но иногда может быть необходимо, чтобы избежать проблем с памятью :

 A <- X[[1]] ; B <- X[[2]] #make the data
list.names <- c("A","B")

for (i in list.names){
    tmp <- get(i)
    tmp$newstuff <- "profit"
    assign(i,tmp)
    rm(tmp)
}
  

Убедитесь, что вы хорошо осведомлены о значении этого кода, поскольку вы работаете в глобальной среде. Если вам нужно делать это чаще, возможно, вы захотите вместо этого работать со средами :

 my.env <- new.env() # make the environment
my.env$A <- X[[1]];my.env$B <- X[[2]] # put vars in environment

for (i in list.names){
    tmp <- get(i,envir=my.env)
    tmp$newstuff <- "profit"
    assign(i,tmp,envir=my.env)
    rm(tmp)
}
my.env$A
my.env$B