una - graficos en r
Acceder a aplicar nombres de índice dentro de FUN (10)
¿Hay alguna forma de obtener el nombre del índice de la lista en mi función lapply ()?
n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?/n" })
Pregunté before si es posible preservar los nombres de los índices en la lista devuelta de lapply (), pero aún no sé si hay una manera fácil de obtener cada nombre de elemento dentro de la función personalizada. Me gustaría evitar invocar los nombres en sí mismos, prefiero obtener el nombre en los parámetros de la función.
Desafortunadamente, lapply
solo te da los elementos del vector que pasas. El trabajo habitual es pasarle los nombres o índices del vector en lugar del vector en sí.
Pero tenga en cuenta que siempre puede pasar argumentos adicionales a la función, por lo que lo siguiente funciona:
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))
Aquí utilizo lapply
sobre los índices de x
, pero también paso en x
y los nombres de x
. Como puede ver, el orden de los argumentos de la función puede ser cualquier cosa, lapply
pasará en el "elemento" (aquí el índice) al primer argumento no especificado entre los adicionales. En este caso, especifico y
y n
, así que solo i
queda ...
Que produce lo siguiente:
[[1]]
[1] "a 11"
[[2]]
[1] "b 12"
[[3]]
[1] "c 13"
ACTUALIZACIÓN Ejemplo simple, mismo resultado:
lapply(seq_along(x), function(i) paste(names(x)[[i]], x[[i]]))
Aquí la función usa la variable "global" x
y extrae los nombres en cada llamada.
Esto básicamente utiliza la misma solución que Tommy, pero con Map()
, no es necesario acceder a las variables globales que almacenan los nombres de los componentes de la lista.
> 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
O, si prefiere mapply()
> mapply(function(x, i) paste(i, x), x, names(x))
a b c
"a 11" "b 12" "c 13"
He tenido el mismo problema muchas veces ... Empecé a utilizarlo de otra manera ... En vez de usar lapply
, comencé a usar mapply
n = names(mylist)
mapply(function(list.elem, names) { }, list.elem = mylist, names = n)
La respuesta de Tommy se aplica a los vectores con nombre, pero me dio la idea de que estaba interesado en las listas. Y parece que estaba haciendo un fin porque estaba haciendo referencia a "x" del entorno de llamada. Esta función utiliza solo los parámetros que se pasaron a la función y, por lo tanto, no hace suposiciones sobre el nombre de los objetos que se pasaron:
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
Mi respuesta va en la misma dirección que Tommy y los caracales, pero evita tener que guardar la lista como un objeto adicional.
lapply(seq(3), function(i, y=list(a=14,b=15,c=16)) { paste(names(y)[[i]], y[[i]]) })
Resultado:
[[1]]
[1] "a 14"
[[2]]
[1] "b 15"
[[3]]
[1] "c 16"
Esto da la lista como un argumento con nombre a FUN (en lugar de aplicar). lapply solo tiene que iterar sobre los elementos de la lista (tenga cuidado de cambiar este primer argumento para aplicar cuando cambie la longitud de la lista).
Nota: Dar la lista directamente para borrar como un argumento adicional también funciona:
lapply(seq(3), function(i, y) { paste(names(y)[[i]], y[[i]]) }, y=list(a=14,b=15,c=16))
Podría intentar usar imap()
desde el paquete purrr
.
De la documentación:
imap (x, ...) es abreviado para map2 (x, names (x), ...) si x tiene nombres, o map2 (x, seq_along (x), ...) si no lo tiene.
Entonces, puedes usarlo de esa manera:
library(purrr)
myList <- list(a=11,b=12,c=13)
imap(myList, function(x, y) paste(x, y))
Lo cual le dará el siguiente resultado:
$a
[1] "11 a"
$b
[1] "12 b"
$c
[1] "13 c"
Simplemente escriba su propia función de lapply
personalizada
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)
}
Entonces use esto:
lapply2(letters, function(x, i) paste(x, i))
Solo bucle en los nombres.
sapply(names(mylist), function(n) {
doSomething(mylist[[n]])
cat(n, ''/n'')
}
Tanto @caracals como @Tommy son buenas soluciones y este es un ejemplo que incluye list
''s y data.frame
''s.
r
es una list
de list
''s y data.frame
''s ( dput(r[[1]]
al final).
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
El objetivo es unlist
todas las listas, poniendo la secuencia de los nombres de la list
como columnas para identificar el caso.
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"
Desinscribe las listas, pero no las de data.frame
.
ra=Reduce(rbind,Map(function(x,y) cbind(case=x,y),names(r),r))
Map
pone la secuencia de nombres como una columna. Reduce
join all data.frame
''s.
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"))
ACTUALIZACIÓN para R versión 3.2
Descargo de responsabilidad: este es un truco hacky, y puede dejar de funcionar en los próximos lanzamientos.
Puedes obtener el índice usando esto:
> lapply(list(a=10,b=20), function(x){parent.frame()$i[]})
$a
[1] 1
$b
[1] 2
Nota: se requiere []
para que esto funcione, ya que engaña a R para que piense que el símbolo i
(que reside en el marco de evaluación de lapply
) puede tener más referencias, activando así la duplicación diferida de la misma. Sin él, R no guardará copias separadas de i
:
> lapply(list(a=10,b=20), function(x){parent.frame()$i})
$a
[1] 2
$b
[1] 2
Se pueden usar otros trucos exóticos, como function(x){parent.frame()$i+0}
o function(x){--parent.frame()$i}
.
Impacto en el rendimiento
¿La duplicación forzada causará la pérdida de rendimiento? ¡Sí! aquí están los puntos de referencia:
> 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
Conclusión
Esta respuesta solo muestra que NO debes usar esto ... No solo tu código será más legible si encuentras otra solución como la anterior de Tommy, y es más compatible con lanzamientos futuros, también corres el riesgo de perder las optimizaciones que el equipo central ha trabajado duro para ¡desarrollar!
Los trucos de las versiones antiguas ya no funcionan:
> lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
Resultado:
$a
[1] 1
$b
[1] 2
$c
[1] 3
Explicación: lapply
crea llamadas de la forma FUN(X[[1L]], ...)
, FUN(X[[2L]], ...)
etc. Así que el argumento que pasa es X[[i]]
donde i
es el índice actual en el ciclo. Si obtenemos esto antes de que sea evaluado (es decir, si usamos el substitute
), obtenemos la expresión no evaluada X[[i]]
. Esta es una llamada a la función [[
, con argumentos X
(un símbolo) i
(un número entero). Entonces substitute(x)[[3]]
devuelve precisamente este entero.
Al tener el índice, puede acceder a los nombres trivialmente, si lo guarda primero así:
L <- list(a=10,b=10,c=10)
n <- names(L)
lapply(L, function(x)n[substitute(x)[[3]]])
Resultado:
$a
[1] "a"
$b
[1] "b"
$c
[1] "c"
O usando este segundo truco: :-)
lapply(list(a=10,b=10,c=10), function(x)names(eval(sys.call(1)[[2]]))[substitute(x)[[3]]])
(el resultado es el mismo).
Explicación 2: sys.call(1)
devuelve lapply(...)
, de modo que sys.call(1)[[2]]
es la expresión utilizada como argumento de lista para lapply
. Pasar esto a eval
crea un objeto legítimo al que los names
pueden acceder. Tricky, pero funciona.
Bonificación: una segunda forma de obtener los nombres:
lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
Tenga en cuenta que X
es un objeto válido en el marco principal de FUN
, y hace referencia al argumento list de lapply
, para que podamos acceder a él con eval.parent
.