2012-02-12 14 views
6

Tengo una lista en la que cada elemento de la lista es una tabla de frecuencia de palabras derivada del uso de "tabla()" en un texto de muestra diferente. Cada tabla es, por lo tanto, una longitud diferente. Quiero convertir ahora la lista en un único marco de datos en el que cada columna es una palabra, cada fila es un texto de muestra. Aquí es un ejemplo ficticio de mis datos:Combinar tablas de frecuencia en un único marco de datos

t1<-table(strsplit(tolower("this is a test in the event of a real word file you would see many more words here"), "\\W")) 

t2<-table(strsplit(tolower("Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal"), "\\W")) 

t3<-table(strsplit(tolower("Ask not what your country can do for you - ask what you can do for your country"), "\\W")) 

myList <- list(t1, t2, t3) 

así, uno tiene este tipo de estructura:

> class(myList[[3]]) 
[1] "table" 

> myList[[3]] 

     ask  can country  do  for  not what  you your 
    2  2  2  2  2  2  1  2  2  2 

Ahora necesito para convertir esta lista (miLista) en una sola trama de datos. Pensé que podría hacer esto con plyr, en la línea de lo que se hace aquí (http://ryouready.wordpress.com/2009/01/23/r-combining-vectors-or-data-frames-of-unequal- length-in-one-data-frame /), por ejemplo

library(plyr) 
l <- myList 
do.call(rbind.fill, l) 

Pero parece que mis objetos de "mesa" no funcionan bien. Traté de convertirlos a dfs y también a vectores, pero nada de eso funcionó del todo bien.

+0

oh wait, en mi respuesta asumí que querías una columna de data.frame separada para cada tabla ... ¿Buscabas un formato diferente a ese? –

Respuesta

4
freqs.list <- mapply(data.frame,Words=seq_along(myList),myList,SIMPLIFY=FALSE,MoreArgs=list(stringsAsFactors=FALSE)) 
freqs.df <- do.call(rbind,freqs.list) 
res <- reshape(freqs.df,timevar="Words",idvar="Var1",direction="wide") 
head(res) 
+0

Gregory, esta solución es la más eficiente, ¡gracias! – litlogger

1

Aquí hay una manera poco elegante que hace el trabajo. Estoy seguro de que hay un 1-liner por ahí sólo para esto, pero no sé cuando:

myList <- list(t1=t1, t2=t2, t3=t3) 
    myList <- lapply(myList,as.data.frame,stringsAsFactors = FALSE) 
    Words <- unique(unlist(lapply(myList,function(x) x[,1]))) 
    DFmerge <- data.frame(Words=Words) 
    for (i in 1:3){ 
     DFmerge <- merge(DFmerge,myList[[i]],by.x="Words",by.y="Var1",all.x=TRUE) 
    } 
    colnames(DFmerge) <- c("Words","t1","t2","t3") 

Y mirando a su alrededor un poco más, aquí hay otra manera que da una salida más similar a la de la entrada de blog relacionado : [Editar: trabaja ahora]

myList <- list(t1=t1, t2=t2, t3=t3) 
    myList <- lapply(myList,function(x) { 
     A <- as.data.frame(matrix(unlist(x),nrow=1)) 
     colnames(A) <- names(x) 
     A[,colnames(A) != ""] 
     } 
    ) 
    do.call(rbind.fill,myList) 

también feo, así que tal vez una respuesta mejor todavía va a llegar.

+0

Gracias Tim, esperaba evitar el bucle for, pero parece que hace el trabajo bien. Luego puedo transponer el df y hacer una pequeña poda para que las palabras sean nombres de columna. . . Aún así, me parece que debería haber una solución basada en plyr. . . . – litlogger

+0

@litlogger el segundo método, todavía feo, ahora funciona y evita un ciclo for –

+0

y debo mencionar, los signos de puntuación, p. '" - "' se convierte en '" "' en 'names (x)', y esto causaba errores para 'rbind.fill()'. Los eché en la función anónima 'lapply'. Solo FYI, en caso de que no se desee –

7

1. zoo. El paquete del zoo tiene una función de combinación de múltiples vías que puede hacer esto de forma compacta. El lapply convierte cada componente de myList a un objeto de zoológico y entonces simplemente se funden todos ellos:

# optionally add nice names to the list 
names(myList) <- paste("t", seq_along(myList), sep = "") 

library(zoo) 
fz <- function(x)with(as.data.frame(x, stringsAsFactors=FALSE), zoo(Freq, Var1))) 
out <- do.call(merge, lapply(myList, fz)) 

Los rendimientos por encima de una serie de zoo multivariante en la que los "tiempos" son "a", "ago", etc. pero si un dato el resultado del cuadro fue deseado, entonces es solo una cuestión de as.data.frame(out).

2. Reduce. Aquí hay una segunda solución. Utiliza Reduce en el núcleo de R.

merge1 <- function(x, y) merge(x, y, by = 1, all = TRUE) 
out <- Reduce(merge1, lapply(myList, as.data.frame, stringsAsFactors = FALSE)) 

# optionally add nice names 
colnames(out)[-1] <- paste("t", seq_along(myList), sep = "") 

3. xtabs. Éste agrega nombres a la lista y luego extrae las frecuencias, los nombres y los grupos como un vector largo cada uno volver a ponerlos juntos usando xtabs:

names(myList) <- paste("t", seq_along(myList)) 

xtabs(Freq ~ Names + Group, data.frame(
    Freq = unlist(lapply(myList, unname)), 
    Names = unlist(lapply(myList, names)), 
    Group = rep(names(myList), sapply(myList, length)) 
)) 

Benchmark

Evaluación comparativa de algunas de las soluciones utilizando la rbenchmark En el paquete obtenemos lo siguiente que indica que la solución de zoo es la más rápida en los datos de muestra y posiblemente también la más simple.

> t1<-table(strsplit(tolower("this is a test in the event of a real word file you would see many more words here"), "\\W")) 
> t2<-table(strsplit(tolower("Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal"), "\\W")) 
> t3<-table(strsplit(tolower("Ask not what your country can do for you - ask what you can do for your country"), "\\W")) 
> myList <- list(t1, t2, t3) 
> 
> library(rbenchmark) 
> library(zoo) 
> names(myList) <- paste("t", seq_along(myList), sep = "") 
> 
> benchmark(xtabs = { 
+ names(myList) <- paste("t", seq_along(myList)) 
+ xtabs(Freq ~ Names + Group, data.frame(
+ Freq = unlist(lapply(myList, unname)), 
+ Names = unlist(lapply(myList, names)), 
+ Group = rep(names(myList), sapply(myList, length)) 
+)) 
+ }, 
+ zoo = { 
+ fz <- function(x) with(as.data.frame(x, stringsAsFactors=FALSE), zoo(Freq, Var1)) 
+ do.call(merge, lapply(myList, fz)) 
+ }, 
+ Reduce = { 
+ merge1 <- function(x, y) merge(x, y, by = 1, all = TRUE) 
+ Reduce(merge1, lapply(myList, as.data.frame, stringsAsFactors = FALSE)) 
+ }, 
+ reshape = { 
+ freqs.list <- mapply(data.frame,Words=seq_along(myList),myList,SIMPLIFY=FALSE,MoreArgs=list(stringsAsFactors=FALSE)) 
+ freqs.df <- do.call(rbind,freqs.list) 
+ reshape(freqs.df,timevar="Words",idvar="Var1",direction="wide") 
+ }, replications = 10, order = "relative", columns = c("test", "replications", "relative")) 
    test replications relative 
2  zoo   10 1.000000 
4 reshape   10 1.090909 
1 xtabs   10 1.272727 
3 Reduce   10 1.272727 

AÑADIDO: second solution.

AGREGADO: tercera solución.

AÑADIDO: benchmark.

+0

Gracias G. Pero cuando ejecuto el ejemplo del código, aparece un error: Error en eval (sustituto (expr), datos, enclos = parent.frame()): numérico 'envir' arg no de longitud uno – litlogger

+0

@litlogger, lo he arreglado. –

Cuestiones relacionadas