¿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,
lapplysolo 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
lapplysobre los índices dex, pero también pasoxy los nombres dex. Como puede ver, el orden de los argumentos de la función puede ser cualquier cosa:lapplypasará el "elemento" (aquí el índice) al primer argumento no especificado entre los adicionales. En este caso, especificoyyn, así que soloiqueda ...Lo que produce lo siguiente:
ACTUALIZACIÓN Ejemplo más simple, mismo resultado:
Aquí la función utiliza la variable "global"
xy extrae los nombres en cada llamada.fuente
ylugar dexpara 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 laSIMPLIFYopció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:
lapplycrea llamadas del formularioFUN(X[[1L]], ...),FUN(X[[2L]], ...)etc. Entonces, el argumento que pasa esX[[i]]dóndeiestá 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 aevalcrea un objeto legítimo al quenamespuede acceder. Difícil, pero funciona.Bono: una segunda forma de obtener los nombres:
Tenga en cuenta que
Xes 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 usarmapplyfuente
Puedes intentar usar
imap()desdepurrrpaquete.De la documentación:
Entonces, puedes usarlo de esa manera:
Lo que te dará el siguiente resultado:
fuente
Solo ingrese los nombres.
fuente
mylistdentro 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 ...lapplysiempre agrega los nombres dexal 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.res unlistdelist´s ydata.frame´s (dput(r[[1]]al final).El objetivo es
unlisttodas las listas, colocando la secuencia delistlos nombres como columnas para identificar el caso.Deslista las listas pero no las
data.frame´s.Mappone 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
lapplyfunción personalizadaLuego use así:
fuente