Подтвердить что ты не робот

Доступ к индексам доступа в FUN

Есть ли способ получить имя индекса списка в моей функции lapply()?

n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })

Я спросил до, если можно сохранить имена индексов в возвращенном списке lapply(), но я до сих пор не знаю, есть ли простой способ введите имя каждого элемента внутри пользовательской функции. Я бы хотел, чтобы не называть себя именами самих себя, я бы предпочел получить имя в параметрах функции.

4b9b3361

Ответ 1

К сожалению, lapply предоставляет только элементы вектора, который вы передаете. Обычным обходом является передача им имен или индексов вектора вместо самого вектора.

Но обратите внимание, что вы всегда можете передать дополнительные аргументы функции, поэтому работает следующее:

x <- list(a=11,b=12,c=13) # Changed to list to address concerns in commments
lapply(seq_along(x), function(y, n, i) { paste(n[[i]], y[[i]]) }, y=x, n=names(x))

Здесь я использую lapply по индексам x, но также передаю в x и именах x. Как вы можете видеть, порядок аргументов функции может быть любым - lapply будет проходить в "элементе" (здесь индекс) до первого аргумента, не указанного среди дополнительных. В этом случае я указываю y и n, поэтому осталось только i...

Что дает следующее:

[[1]]
[1] "a 11"

[[2]]
[1] "b 12"

[[3]]
[1] "c 13"

ОБНОВЛЕНИЕ. Простой пример, тот же результат:

lapply(seq_along(x), function(i) paste(names(x)[[i]], x[[i]]))

Здесь функция использует "глобальную" переменную x и извлекает имена в каждом вызове.

Ответ 2

В основном используется тот же обходной путь, что и Tommy, но с Map() нет необходимости обращаться к глобальным переменным, которые хранят имена компонентов списка.

> x <- list(a=11, b=12, c=13)
> Map(function(x, i) paste(i, x), x, names(x))
$a
[1] "a 11"

$b
[1] "b 12"

$c
[1] "c 13

Или, если вы предпочитаете mapply()

> mapply(function(x, i) paste(i, x), x, names(x))
     a      b      c 
"a 11" "b 12" "c 13"

Ответ 3

ОБНОВЛЕНИЕ для версии R версии 3.2

Отказ от ответственности: это хакерский трюк и может перестать работать в следующих выпусках.

Вы можете получить индекс, используя это:

> lapply(list(a=10,b=20), function(x){parent.frame()$i[]})
$a
[1] 1

$b
[1] 2

Примечание: для этого требуется [], поскольку он обманывает R, полагая, что символ i (находящийся в рамке оценки lapply) может иметь больше ссылок, тем самым активируя ленивое дублирование Это. Без него R не будет хранить отдельные копии i:

> lapply(list(a=10,b=20), function(x){parent.frame()$i})
$a
[1] 2

$b
[1] 2

Можно использовать другие экзотические трюки, например function(x){parent.frame()$i+0} или function(x){--parent.frame()$i}.

Эффективное воздействие

Будет ли принудительное дублирование вызывать потерю производительности? Да! здесь приведены этапы:

> x <- as.list(seq_len(1e6))

> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.38 0.00 2.37
> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.45 0.00 2.45
> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.41 0.00 2.41
> y[[2]]
[1] 2

> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
1.92 0.00 1.93
> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
2.07 0.00 2.09
> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
1.89 0.00 1.89
> y[[2]]
[1] 1000000

Заключение

Этот ответ просто показывает, что вы НЕ должны использовать это... Не только ваш код будет более читабельным, если вы найдете другое решение, такое как Tommy выше, и более совместимы с будущими версиями, вы также рискуете потерять оптимизацию, много работали над развитием!


Трюки старой версии, более не работающие:

> lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])

Результат:

$a
[1] 1

$b
[1] 2

$c
[1] 3

Объяснение: lapply создает вызовы формы FUN(X[[1L]], ...), FUN(X[[2L]], ...) и т.д. Таким образом, аргумент, который он передает, X[[i]], где i - текущий индекс в цикле. Если мы получим это до его оценки (т.е. Если мы используем substitute), получим неоценимое выражение X[[i]]. Это вызов функции [[ с аргументами X (символ) и i (целое число). Таким образом, substitute(x)[[3]] возвращает именно это целое число.

Имея индекс, вы можете получить доступ к именам тривиально, если сначала сохранить его следующим образом:

L <- list(a=10,b=10,c=10)
n <- names(L)
lapply(L, function(x)n[substitute(x)[[3]]])

Результат:

$a
[1] "a"

$b
[1] "b"

$c
[1] "c"

Или используя этот второй трюк:: -)

lapply(list(a=10,b=10,c=10), function(x)names(eval(sys.call(1)[[2]]))[substitute(x)[[3]]])

(результат тот же).

Объяснение 2: sys.call(1) возвращает lapply(...), так что sys.call(1)[[2]] - это выражение, используемое в качестве аргумента списка для lapply. Передача этого значения в eval создает законный объект, к которому может обращаться names. Tricky, но он работает.

Бонус: второй способ получить имена:

lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])

Обратите внимание, что X является допустимым объектом в родительском фрейме FUN и ссылается на аргумент списка lapply, поэтому мы можем перейти к нему с помощью eval.parent.

Ответ 4

У меня была такая же проблема много раз... Я начал использовать другой способ... Вместо использования lapply, я начал использовать mapply

n = names(mylist)
mapply(function(list.elem, names) { }, list.elem = mylist, names = n)

Ответ 5

Просто укажите имена.

sapply(names(mylist), function(n) { 
    doSomething(mylist[[n]])
    cat(n, '\n')
}

Ответ 6

Вы можете попробовать использовать imap() из пакета purrr.

Из документации:

imap (x,...) - сокращение от map2 (x, names (x),...), если x имеет имена, или map2 (x, seq_along (x),...), если его нет.

Итак, вы можете использовать это так:

library(purrr)
myList <- list(a=11,b=12,c=13) 
imap(myList, function(x, y) paste(x, y))

Что даст вам следующий результат:

$a
[1] "11 a"

$b
[1] "12 b"

$c
[1] "13 c"

Ответ 7

Ответ Tommy применяется к именованным векторам, но я понял, что вас интересуют списки. И кажется, что он делает конец, потому что он ссылается на "х" из вызывающей среды. Эта функция использует только параметры, которые были переданы функции, и поэтому не делает никаких предположений об имени переданных объектов:

x <- list(a=11,b=12,c=13)
lapply(x, function(z) { attributes(deparse(substitute(z)))$names  } )
#--------
$a
NULL

$b
NULL

$c
NULL
#--------
 names( lapply(x, function(z) { attributes(deparse(substitute(z)))$names  } ))
#[1] "a" "b" "c"
 what_is_my_name <- function(ZZZ) return(deparse(substitute(ZZZ)))
 what_is_my_name(X)
#[1] "X"
what_is_my_name(ZZZ=this)
#[1] "this"
 exists("this")
#[1] FALSE

Ответ 8

Мой ответ идет в том же направлении, что и Томми и каракалы, но избегает необходимости сохранять список в качестве дополнительного объекта.

lapply(seq(3), function(i, y=list(a=14,b=15,c=16)) { paste(names(y)[[i]], y[[i]]) })

Результат:

[[1]]
[1] "a 14"

[[2]]
[1] "b 15"

[[3]]
[1] "c 16"

Это дает список как именованный аргумент для FUN (вместо этого для привязки). lapply должен только перебирать элементы списка (будьте осторожны, чтобы изменить этот первый аргумент на привязку при изменении длины списка).

Примечание. Приведение списка непосредственно к вставке в качестве дополнительного аргумента также работает:

lapply(seq(3), function(i, y) { paste(names(y)[[i]], y[[i]]) }, y=list(a=14,b=15,c=16))

Ответ 9

И @caracals, и @Tommy являются хорошими решениями, и это пример, включающий list и data.frame.
r - это list list и data.frame (dput(r[[1]] в конце).

names(r)
[1] "todos"  "random"
r[[1]][1]
$F0
$F0$rst1
   algo  rst  prec  rorac prPo pos
1  Mean 56.4 0.450 25.872 91.2 239
6  gbm1 41.8 0.438 22.595 77.4 239
4  GAM2 37.2 0.512 43.256 50.0 172
7  gbm2 36.8 0.422 18.039 85.4 239
11 ran2 35.0 0.442 23.810 61.5 239
2  nai1 29.8 0.544 52.281 33.1 172
5  GAM3 28.8 0.403 12.743 94.6 239
3  GAM1 21.8 0.405 13.374 68.2 239
10 ran1 19.4 0.406 13.566 59.8 239
9  svm2 14.0 0.385  7.692 76.2 239
8  svm1  0.8 0.359  0.471 71.1 239

$F0$rst5
   algo  rst  prec  rorac prPo pos
1  Mean 52.4 0.441 23.604 92.9 239
7  gbm2 46.4 0.440 23.200 83.7 239
6  gbm1 31.2 0.416 16.421 79.5 239
5  GAM3 28.8 0.403 12.743 94.6 239
4  GAM2 28.2 0.481 34.815 47.1 172
11 ran2 26.6 0.422 18.095 61.5 239
2  nai1 23.6 0.519 45.385 30.2 172
3  GAM1 20.6 0.398 11.381 75.7 239
9  svm2 14.4 0.386  8.182 73.6 239
10 ran1 14.0 0.390  9.091 64.4 239
8  svm1  6.2 0.370  3.584 72.4 239

Цель состоит в том, чтобы unlist список всех списков, поместив последовательность имен list в виде столбцов для идентификации дела.

r=unlist(unlist(r,F),F)
names(r)
[1] "todos.F0.rst1"  "todos.F0.rst5"  "todos.T0.rst1"  "todos.T0.rst5"  "random.F0.rst1" "random.F0.rst5"
[7] "random.T0.rst1" "random.T0.rst5"

Unlist списки, но не data.frame.

ra=Reduce(rbind,Map(function(x,y) cbind(case=x,y),names(r),r))

Map помещает последовательность имен в виде столбца. Reduce объединение всех data.frame.

head(ra)
            case algo  rst  prec  rorac prPo pos
1  todos.F0.rst1 Mean 56.4 0.450 25.872 91.2 239
6  todos.F0.rst1 gbm1 41.8 0.438 22.595 77.4 239
4  todos.F0.rst1 GAM2 37.2 0.512 43.256 50.0 172
7  todos.F0.rst1 gbm2 36.8 0.422 18.039 85.4 239
11 todos.F0.rst1 ran2 35.0 0.442 23.810 61.5 239
2  todos.F0.rst1 nai1 29.8 0.544 52.281 33.1 172

PS r[[1]]:

    structure(list(F0 = structure(list(rst1 = structure(list(algo = c("Mean", 
    "gbm1", "GAM2", "gbm2", "ran2", "nai1", "GAM3", "GAM1", "ran1", 
    "svm2", "svm1"), rst = c(56.4, 41.8, 37.2, 36.8, 35, 29.8, 28.8, 
    21.8, 19.4, 14, 0.8), prec = c(0.45, 0.438, 0.512, 0.422, 0.442, 
    0.544, 0.403, 0.405, 0.406, 0.385, 0.359), rorac = c(25.872, 
    22.595, 43.256, 18.039, 23.81, 52.281, 12.743, 13.374, 13.566, 
    7.692, 0.471), prPo = c(91.2, 77.4, 50, 85.4, 61.5, 33.1, 94.6, 
    68.2, 59.8, 76.2, 71.1), pos = c(239L, 239L, 172L, 239L, 239L, 
    172L, 239L, 239L, 239L, 239L, 239L)), .Names = c("algo", "rst", 
    "prec", "rorac", "prPo", "pos"), row.names = c(1L, 6L, 4L, 7L, 
    11L, 2L, 5L, 3L, 10L, 9L, 8L), class = "data.frame"), rst5 = structure(list(
        algo = c("Mean", "gbm2", "gbm1", "GAM3", "GAM2", "ran2", 
        "nai1", "GAM1", "svm2", "ran1", "svm1"), rst = c(52.4, 46.4, 
        31.2, 28.8, 28.2, 26.6, 23.6, 20.6, 14.4, 14, 6.2), prec = c(0.441, 
        0.44, 0.416, 0.403, 0.481, 0.422, 0.519, 0.398, 0.386, 0.39, 
        0.37), rorac = c(23.604, 23.2, 16.421, 12.743, 34.815, 18.095, 
        45.385, 11.381, 8.182, 9.091, 3.584), prPo = c(92.9, 83.7, 
        79.5, 94.6, 47.1, 61.5, 30.2, 75.7, 73.6, 64.4, 72.4), pos = c(239L, 
        239L, 239L, 239L, 172L, 239L, 172L, 239L, 239L, 239L, 239L
        )), .Names = c("algo", "rst", "prec", "rorac", "prPo", "pos"
    ), row.names = c(1L, 7L, 6L, 5L, 4L, 11L, 2L, 3L, 9L, 10L, 8L
    ), class = "data.frame")), .Names = c("rst1", "rst5")), T0 = structure(list(
        rst1 = structure(list(algo = c("Mean", "ran1", "GAM1", "GAM2", 
        "gbm1", "svm1", "nai1", "gbm2", "svm2", "ran2"), rst = c(22.6, 
        19.4, 13.6, 10.2, 9.6, 8, 5.6, 3.4, -0.4, -0.6), prec = c(0.478, 
        0.452, 0.5, 0.421, 0.423, 0.833, 0.429, 0.373, 0.355, 0.356
        ), rorac = c(33.731, 26.575, 40, 17.895, 18.462, 133.333, 
        20, 4.533, -0.526, -0.368), prPo = c(34.4, 52.1, 24.3, 40.7, 
        37.1, 3.1, 14.4, 53.6, 54.3, 116.4), pos = c(195L, 140L, 
        140L, 140L, 140L, 195L, 195L, 140L, 140L, 140L)), .Names = c("algo", 
        "rst", "prec", "rorac", "prPo", "pos"), row.names = c(1L, 
        9L, 3L, 4L, 5L, 7L, 2L, 6L, 8L, 10L), class = "data.frame"), 
        rst5 = structure(list(algo = c("gbm1", "ran1", "Mean", "GAM1", 
        "GAM2", "svm1", "nai1", "svm2", "gbm2", "ran2"), rst = c(17.6, 
        16.4, 15, 12.8, 9, 6.2, 5.8, -2.6, -3, -9.2), prec = c(0.466, 
        0.434, 0.435, 0.5, 0.41, 0.8, 0.44, 0.346, 0.345, 0.337), 
            rorac = c(30.345, 21.579, 21.739, 40, 14.754, 124, 23.2, 
            -3.21, -3.448, -5.542), prPo = c(41.4, 54.3, 35.4, 22.9, 
            43.6, 2.6, 12.8, 57.9, 62.1, 118.6), pos = c(140L, 140L, 
            195L, 140L, 140L, 195L, 195L, 140L, 140L, 140L)), .Names = c("algo", 
        "rst", "prec", "rorac", "prPo", "pos"), row.names = c(5L, 
        9L, 1L, 3L, 4L, 7L, 2L, 8L, 6L, 10L), class = "data.frame")), .Names = c("rst1", 
    "rst5"))), .Names = c("F0", "T0"))

Ответ 10

Допустим, мы хотим рассчитать длину каждого элемента.

mylist <- list(a=1:4,b=2:9,c=10:20)
mylist

$a
[1] 1 2 3 4

$b
[1] 2 3 4 5 6 7 8 9

$c
 [1] 10 11 12 13 14 15 16 17 18 19 20

Если цель состоит в том, чтобы просто пометить результирующие элементы, то работает lapply(mylist,length) или ниже.

sapply(mylist,length,USE.NAMES=T)

 a  b  c 
 4  8 11 

Если цель состоит в том, чтобы использовать метку внутри функции, тогда mapply() полезен, зацикливаясь на двух объектах; элементы списка и имена списков.

fun <- function(x,y) paste0(length(x),"_",y)
mapply(fun,mylist,names(mylist))

     a      b      c 
 "4_a"  "8_b" "11_c" 

Ответ 11

Просто напишите свою собственную функцию lapply

lapply2 <- function(X, FUN){
  if( length(formals(FUN)) == 1 ){
    # No index passed - use normal lapply
    R = lapply(X, FUN)
  }else{
    # Index passed
    R = lapply(seq_along(X), FUN=function(i){
      FUN(X[[i]], i)
    })
  }

  # Set names
  names(R) = names(X)
  return(R)
}

Затем используйте так:

lapply2(letters, function(x, i) paste(x, i))