Me gustaría construir un marco de datos fila por fila en R.He hecho algunas búsquedas y todo lo que se me ocurrió es la sugerencia de crear una lista vacía, mantener un índice de lista escalar y luego agregarlo cada vez a la lista. un marco de datos de una sola fila y avance el índice de la lista en uno. Finalmente, do.call(rbind,)
en la lista.
Si bien esto funciona, parece muy engorroso. ¿No hay una forma más fácil de lograr el mismo objetivo?
Obviamente, me refiero a casos en los que no puedo usar alguna apply
función y explícitamente necesito crear el marco de datos fila por fila. Al menos, ¿hay alguna manera de llegar push
al final de una lista en lugar de realizar un seguimiento explícito del último índice utilizado?
append()
[que probablemente debería llamarse insertar] oc()
agregar elementos al final de una lista, aunque no lo ayudará aquí.lapply()
,Map()
y así sucesivamente, pero también es posible que desee echar un vistazo aaggregate()
,dapply() {heR.Misc}
ycast() {reshape}
para ver si sus tareas no pueden ser manejados por éstos funciones (todas devuelven marcos de datos).Respuestas:
Puede hacerlos crecer fila por fila agregando o usando
rbind()
.Eso no significa que debas hacerlo. Las estructuras de crecimiento dinámico es una de las formas menos eficientes de codificar en R.
Si puede, asigne todo su data.frame por adelantado:
y luego, durante sus operaciones, inserte una fila a la vez
Eso debería funcionar para data.frame arbitrario y ser mucho más eficiente. Si sobrepasa N, siempre puede reducir las filas vacías al final.
fuente
data.table
parece ser incluso más rápido que la preasignación utilizando data.frames. Probando aquí: stackoverflow.com/a/11486400/636656Se pueden agregar filas a
NULL
:por ejemplo
fuente
sapply
(o vectorizar) y transponer.Este es un ejemplo tonto de cómo usar
do.call(rbind,)
en la salida deMap()
[que es similar alapply()
]Utilizo esta construcción con bastante frecuencia.
fuente
La razón por la que me gusta tanto Rcpp es que no siempre entiendo cómo piensa R Core, y con Rcpp, la mayoría de las veces, no tengo que hacerlo.
Hablando filosóficamente, estás en un estado de pecado con respecto al paradigma funcional, que trata de asegurar que cada valor parezca independiente de cualquier otro valor; cambiar un valor nunca debería provocar un cambio visible en otro valor, como ocurre con los punteros que comparten la representación en C.
Los problemas surgen cuando la programación funcional le indica a la pequeña nave que se mueva fuera del camino, y la pequeña nave responde "Soy un faro". Hacer una larga serie de pequeños cambios en un objeto grande que desea procesar mientras tanto lo coloca en el territorio del faro.
En C ++ STL,
push_back()
es una forma de vida. No intenta ser funcional, pero intenta adaptarse a los modismos comunes de programación de manera eficiente. .Con un poco de inteligencia entre bastidores, a veces puede hacer arreglos para tener un pie en cada mundo. Los sistemas de archivos basados en instantáneas son un buen ejemplo (que evolucionó a partir de conceptos como los montajes de unión, que también cubren ambos lados).
Si R Core quisiera hacer esto, el almacenamiento vectorial subyacente podría funcionar como un montaje de unión. Una referencia al almacenamiento de vectores puede ser válida para subíndices
1:N
, mientras que otra referencia al mismo almacenamiento es válida para subíndices1:(N+1)
. Podría haber un almacenamiento reservado que aún no esté referenciado de manera válida por nada más que conveniente para un archivopush_back()
. No viola el concepto funcional al agregar fuera del rango que cualquier referencia existente considera válida.Al agregar filas de forma incremental, te quedas sin almacenamiento reservado. Deberá crear nuevas copias de todo, con el almacenamiento multiplicado por algún incremento. Las implementaciones de STL que utilizo tienden a multiplicar el almacenamiento por 2 al extender la asignación. Pensé haber leído en R Internals que hay una estructura de memoria donde el almacenamiento se incrementa en un 20%. De cualquier manera, las operaciones de crecimiento ocurren con frecuencia logarítmica en relación con el número total de elementos agregados. Sobre una base amortizada, esto suele ser aceptable.
En cuanto a los trucos entre bastidores, he visto peores. Cada vez que
push_back()
ingrese una nueva fila en el marco de datos, se deberá copiar una estructura de índice de nivel superior. La nueva fila podría agregarse a la representación compartida sin afectar ningún valor funcional anterior. Ni siquiera creo que complicaría mucho al recolector de basura; ya que no propongo quepush_front()
todas las referencias sean referencias de prefijo al frente del almacenamiento de vectores asignado.fuente
La respuesta de Dirk Eddelbuettel es la mejor; aquí solo noto que puede salirse con la suya sin especificar previamente las dimensiones del marco de datos o los tipos de datos, lo que a veces es útil si tiene varios tipos de datos y muchas columnas:
fuente
df<-rbind(df, row2)
?Encontré esta forma de crear un marco de datos sin formato sin matriz.
Con nombre de columna automático
Con nombre de columna
fuente
Si tiene vectores destinados a convertirse en filas, concatenarlos usando
c()
, pasarlos a una matriz fila por fila y convertir esa matriz en un marco de datos.Por ejemplo, filas
se puede convertir a un marco de datos así:
Es cierto que veo 2 limitaciones principales: (1) esto solo funciona con datos monomodo y (2) debe conocer sus # columnas finales para que esto funcione (es decir, supongo que no está trabajando con un matriz irregular cuya mayor longitud de fila se desconoce a priori ).
Esta solución parece simple, pero según mi experiencia con las conversiones de tipos en R, estoy seguro de que crea nuevos desafíos en el futuro. ¿Alguien puede comentar sobre esto?
fuente
Dependiendo del formato de su nueva fila, puede usar
tibble::add_row
si su nueva fila es simple y se puede especificar en "pares de valores". O podría usardplyr::bind_rows
"una implementación eficiente del patrón común de do.call (rbind, dfs)".fuente