¿Hay alguna manera 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é antes si es posible preservar los nombres de índice en la lista devuelta lapply () , pero todavía no sé si hay una manera fácil de recuperar cada nombre de elemento dentro de la función personalizada. Me gustaría evitar llamar a lapply en los nombres mismos, prefiero obtener el nombre en los parámetros de la función.
Respuestas:
Desafortunadamente,
lapply
solo te da los elementos del vector que le pasas. La solución 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:
Aquí uso
lapply
sobre los índices dex
, pero también pasox
y los nombres dex
. Como puede ver, el orden de los argumentos de la función puede ser cualquier cosa:lapply
pasará el "elemento" (aquí el índice) al primer argumento no especificado entre los adicionales. En este caso, especificoy
yn
, así que soloi
queda ...Lo que produce lo siguiente:
ACTUALIZACIÓN Ejemplo más simple, mismo resultado:
Aquí la función utiliza la variable "global"
x
y extrae los nombres en cada llamada.fuente
y
lugar dex
para que sea (con suerte) más claro que la función puede llamar a sus argumentos de cualquier manera. También cambió los valores de vector a11,12,13
.Básicamente, utiliza la misma solución alternativa que Tommy, pero con
Map()
, no hay necesidad de acceder a las variables globales que almacenan los nombres de los componentes de la lista.O si lo prefieres
mapply()
fuente
mapply()
, observe laSIMPLIFY
opción, que por defecto es verdadera. En mi caso, eso convirtió todo en una matriz grande cuando solo quería aplicar una lista simple. Establecerlo enF
(dentro demapply()
) lo hizo funcionar según lo previsto.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.
Puede obtener el índice usando esto:
Nota:
[]
es necesario para que esto funcione, ya que engaña a R para que piense que el símboloi
(que reside en el marco de evaluación delapply
) puede tener más referencias, lo que activa la duplicación perezosa de este. Sin ella, R no guardará copias separadas dei
:Se pueden usar otros trucos exóticos, como
function(x){parent.frame()$i+0}
ofunction(x){--parent.frame()$i}
.Impacto en el rendimiento
¿La duplicación forzada causará pérdida de rendimiento? ¡Si! Aquí están los puntos de referencia:
Conclusión
Esta respuesta solo muestra que NO debe usar esto ... No solo su código será más legible si encuentra otra solución como la de Tommy anterior, y más compatible con futuras versiones, también corre el riesgo de perder las optimizaciones que el equipo central ha trabajado duro para ¡desarrollar!
Trucos de versiones anteriores, que ya no funcionan:
Resultado:
Explicación:
lapply
crea llamadas del formularioFUN(X[[1L]], ...)
,FUN(X[[2L]], ...)
etc. Entonces, el argumento que pasa esX[[i]]
dóndei
está el índice actual en el bucle. Si obtenemos esto antes de que sea evaluado (es decir, si lo usamossubstitute
), obtenemos la expresión no evaluadaX[[i]]
. Esta es una llamada a la[[
función, con argumentosX
(un símbolo) yi
(un número entero). Entoncessubstitute(x)[[3]]
devuelve precisamente este entero.Al tener el índice, puede acceder a los nombres trivialmente, si lo guarda primero así:
Resultado:
O usando este segundo truco: :-)
(El resultado es el mismo).
Explicación 2:
sys.call(1)
devuelvelapply(...)
, entonces esasys.call(1)[[2]]
es la expresión utilizada como argumento de lista paralapply
. Pasando esto aeval
crea un objeto legítimo al quenames
puede acceder. Difícil, pero funciona.Bono: una segunda forma de obtener los nombres:
Tenga en cuenta que
X
es un objeto válido en el marco primario deFUN
, y hace referencia al argumento de lista delapply
, por lo que podemos llegar a él coneval.parent
.fuente
lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])
vuelve a ser 3. ¿Explicaría cómo se eligió este 3? y razón de la discrepancia? Es igual a la longitud de la lista, en este caso, 3. Lo siento si esta es una pregunta básica pero me gustaría saber cómo aplicar esto en un caso general.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
funciona ... Comprobaré lo que está sucediendo.lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])
ya no funciona y da un error,Error in eval.parent(quote(names(X)))[substitute(x)[[3]]] : invalid subscript type 'symbol'
¿hay alguna manera fácil de solucionarlo?He tenido el mismo problema muchas veces ... Empecé a usar otra forma ... En lugar de usar
lapply
, comencé a usarmapply
fuente
Puedes intentar usar
imap()
desdepurrr
paquete.De la documentación:
Entonces, puedes usarlo de esa manera:
Lo que te dará el siguiente resultado:
fuente
Solo ingrese los nombres.
fuente
mylist
dentro de la función. Mejor aún por hacerfunction(mylist, nm) ...
La respuesta de Tommy se aplica a los vectores con nombre, pero tengo la idea de que te interesan las listas. Y parece que estaba haciendo una vuelta porque hacía referencia a "x" desde el entorno de llamada. Esta función usa 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:
fuente
NULL
? Entonceslapply(x, function(x) NULL)
da la misma respuesta ...lapply
siempre agrega los nombres dex
al resultado después .Mi respuesta va en la misma dirección que Tommy y los caracales, pero evita tener que guardar la lista como un objeto adicional.
Resultado:
Esto le da a la lista un argumento con nombre a FUN (en lugar de lapply). lapply solo tiene que iterar sobre los elementos de la lista (tenga cuidado de cambiar este primer argumento a lapply cuando cambie la longitud de la lista).
Nota: Dar la lista directamente a lapply como argumento adicional también funciona:
fuente
Tanto @caracals como @Tommy son buenas soluciones y este es un ejemplo que incluye
list
´s ydata.frame
´s.r
es unlist
delist
´s ydata.frame
´s (dput(r[[1]]
al final).El objetivo es
unlist
todas las listas, colocando la secuencia delist
los nombres como columnas para identificar el caso.Deslista las listas pero no las
data.frame
´s.Map
pone la secuencia de nombres como una columna.Reduce
únete a todosdata.frame
.PD
r[[1]]
:fuente
Digamos que queremos calcular la longitud de cada elemento.
Si el objetivo es simplemente etiquetar los elementos resultantes, entonces
lapply(mylist,length)
o debajo funciona.Si el objetivo es usar la etiqueta dentro de la función, entonces
mapply()
es útil haciendo un bucle sobre dos objetos; los elementos de la lista y los nombres de la lista.fuente
@ ferdinand-kraft nos dio un gran truco y luego nos dice que no deberíamos usarlo porque no está documentado y debido a la sobrecarga de rendimiento.
No puedo discutir mucho con el primer punto, pero me gustaría señalar que los gastos generales rara vez deberían ser una preocupación.
definamos las funciones activas para que no tengamos que llamar a la expresión compleja
parent.frame()$i[]
sino que solo.i()
crearemos.n()
para acceder al nombre, que debería funcionar tanto para las funciones base como para las purrr (y probablemente también para la mayoría de las otras).Ahora comparemos una función simple que pega los elementos de un vector a su índice, utilizando diferentes enfoques (estas operaciones pueden, por supuesto, ser vectorizadas usando
paste(vec, seq_along(vec))
pero ese no es el punto aquí).Definimos una función de evaluación comparativa y una función de trazado y trazamos los resultados a continuación:
Creado el 15/11/2019 por el paquete reprex (v0.3.0)
La caída al comienzo del primer gráfico es una casualidad, ignórela.
Vemos que la respuesta elegida es de hecho más rápida, y para una cantidad decente de iteraciones nuestras
.i()
soluciones son de hecho más lentas, la sobrecarga en comparación con la respuesta elegida es aproximadamente 3 veces la sobrecarga de usopurrr::imap()
, y asciende a aproximadamente 25 ms para 30k iteraciones, así que pierdo aproximadamente 1 ms por 1000 iteraciones, 1 segundo por millón. Eso es un pequeño costo de conveniencia en mi opinión.fuente
Simplemente escriba su propia
lapply
función personalizadaLuego use así:
fuente