2010-05-24 26 views
76
df <- data.frame(var1=c('a', 'b', 'c'), var2=c('d', 'e', 'f'), freq=1:3) 

¿Cuál es la forma más sencilla de ampliar las dos primeras columnas de la hoja.de.datos arriba, de manera que cada fila aparece el número de veces especificado en la columna 'freq'?Replicar cada fila de hoja.de.datos y especificar el número de repeticiones para cada fila

En otras palabras, pasar de esto:

df 
    var1 var2 freq 
1 a d 1 
2 b e 2 
3 c f 3 

A esto:

df.expanded 
    var1 var2 
1 a d 
2 b e 
3 b e 
4 c f 
5 c f 
6 c f 

Respuesta

106

He aquí una solución:

df.expanded <- df[rep(row.names(df), df$freq), 1:2] 

Resultado:

var1 var2 
1  a d 
2  b e 
2.1 b e 
3  c f 
3.1 c f 
3.2 c f 
+0

Gran! Siempre me olvido que puedes usar corchetes de esa manera. Sigo pensando en indexar solo para subconjuntos o reordenamientos. Tenía otra solución que es mucho menos elegante y, sin duda, menos eficiente. Podría publicar de todos modos para que otros puedan comparar. – wkmor1

+14

Para 'data.frame' grande, más eficiente es reemplazar' row.names (df) 'con' seq.int (1, nrow (df)) 'o' seq_len (nrow (df)) '. – Marek

+0

Esto funcionó fantásticamente para un gran marco de datos: 1,5 millones de filas, 5 columnas, fueron muy rápido. ¡Gracias! – gabe

13

La solución de @ neilfws funciona muy bien para data.frame s, pero no para data.table s ya que carecen de la propiedad row.names. Este enfoque funciona para ambos:

df.expanded <- df[rep(seq(nrow(df)), df$freq), 1:2] 

Para data.table pesar de que tendrá que añadir with=F y opcionalmente puede quitar df$:

dt <- data.table(df) 
dt.expanded <- dt[rep(seq(.N), freq), !"freq", with=F] 
+6

'.N' ahora está accesible en el primer argumento de' dt [] ', por lo que puede hacer' dt [rep (seq (.N), freq) ,! "freq", con = FALSE] 'o similar. – Frank

+0

Gracias, actualizado. –

+1

otra alternativa: 'df [rep (seq (.N), freq)] [, freq: = NULL]' – Jaap

33

Uso expandRows() del splitstackshape paquete:

library(splitstackshape) 
expandRows(df, "freq") 

Sintaxis simple, muy rápido, funciona en data.frame o data.table.

Resultado:

var1 var2 
1  a d 
2  b e 
2.1 b e 
3  c f 
3.1 c f 
3.2 c f 
2

en caso de tener que hacer esta operación en muy grandes data.frames que recomendaría convertirlo en un data.table y utilizar los siguientes, que debe ejecutar mucho más rápido:

library(data.table) 
dt <- data.table(df) 
dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")] 
dt.expanded[ ,freq := NULL] 
dt.expanded 

ver cuánto más rápido de esta solución es:

df <- data.frame(var1=1:2e3, var2=1:2e3, freq=1:2e3) 
system.time(df.exp <- df[rep(row.names(df), df$freq), 1:2]) 
## user system elapsed 
## 4.57 0.00 4.56 
dt <- data.table(df) 
system.time(dt.expanded <- dt[ ,list(freq=rep(1,freq)),by=c("var1","var2")]) 
## user system elapsed 
## 0.05 0.01 0.06 
+0

Aparece un error: 'Error en rep (1, freq): argumento 'times' inválido'. Y dado que ya hay una información.En la tabla de respuestas a esta pregunta, es posible que desee describir cómo su enfoque es diferente o cuándo es mejor que la respuesta de datos actual. O bien, si no hay una diferencia importante, puede agregarla como comentario a la respuesta existente. –

+0

@SamFirke: Gracias por tu comentario. Extraño, lo intenté de nuevo y no recibo tal error. ¿Usas el 'df' original de la pregunta del OP? Mi respuesta es mejor porque la otra respuesta es un tipo de uso indebido del paquete 'data.table' utilizando la sintaxis' data.frame', consulte las preguntas frecuentes de 'data.table':" En general, es una mala práctica referirse a columnas por número en lugar de nombre ". – vonjd

+1

Gracias por la explicación. Tu código funciona para mí en la muestra 'df' publicada por el OP, pero cuando traté de compararlo en un data.frame más grande, obtuve ese error. El data.frame que utilicé fue: 'set.seed (1) dfbig <- data.frame (var1 = sample (letters, 1000, replace = TRUE), var2 = sample (LETTERS, 1000, replace = TRUE), freq = muestra (1:10, 1000, reemplazar = VERDADERO)) 'En el pequeño marco de datos, la respuesta base funciona bien en mi evaluación comparativa, simplemente no se adapta bien a marcos de datos más grandes. Las otras tres respuestas funcionaron con éxito con este data.frame más grande. –

4

vieja pregunta, nuevo verbo en tidyverse:

library(tidyr) # version >= 0.8.0 
df <- data.frame(var1=c('a', 'b', 'c'), var2=c('d', 'e', 'f'), freq=1:3) 
df %>% 
    uncount(freq) 

e

Cuestiones relacionadas