2012-02-12 15 views
6

Ho una lista in cui ogni voce di elenco è una tabella di frequenza di parole derivata dall'uso di "table()" su un testo di esempio diverso. Ogni tabella è, quindi, di lunghezza diversa. Voglio ora convertire la lista in un singolo frame di dati in cui ogni colonna è una parola ogni riga è un testo di esempio. Ecco un esempio fittizio dei miei dati:Combina tabelle di frequenza in un singolo frame di dati

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) 

così, si ha questo tipo di struttura:

> 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 

ora bisogno di convertire questa lista (myList) in un singolo frame di dati. Pensavo di poterlo fare con plyr, sulla falsariga di ciò che viene fatto qui (http://ryouready.wordpress.com/2009/01/23/r-combining-vectors-or-data-frames-of-unequal- length-in-one-data-frame /), ad es

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

Ma sembra che i miei oggetti "da tavolo" non giochino bene. Ho provato a convertirli in dfs e anche in vettori, ma nessuno di questi ha funzionato correttamente.

+0

oh, aspetta, nella mia risposta ho pensato che volevi una colonna data.frame separata per ogni tabella. Eri in un formato diverso da quello? –

risposta

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, questa soluzione è la più efficiente, grazie! – litlogger

1

Ecco un modo poco elegante per completare il lavoro. Sono sicuro che c'è un 1-liner là fuori solo per questo, ma non so dove sia:

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") 

E guardando intorno a un po 'di più, ecco un altro modo che dà uscita più simile a quello nel post del blog collegato : [Edit: ora funziona]

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) 

anche brutto, quindi forse una risposta migliore sarà ancora venire avanti.

+0

Grazie Tim, speravo di evitare il ciclo for, ma questo sembra aver fatto il lavoro. Posso quindi trasporre il df e fare un po 'di potatura in modo che le parole siano nomi di colonne. . . ancora, mi sembra che ci dovrebbe essere una soluzione basata su plyr. . . . – litlogger

+0

@litlogger il secondo metodo, ancora brutto, ora funziona ed evita un ciclo for –

+0

e dovrei menzionare, i segni di punteggiatura, ad es. '" - "' trasforma in '" "' in 'names (x)', e questo ha causato errori per 'rbind.fill()'. Li ho buttati nella funzione anonima 'lapply'. Solo FYI, nel caso in cui non fosse desiderato –

7

1. zoo. Il pacchetto zoo ha una funzione di fusione multipla che può farlo in modo compatto. Il lapply converte ogni componente di myList a un oggetto zoo e poi semplicemente tutti merge:

# 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)) 

I suddetti rendimenti una serie zoo multivariata in cui i "tempi" sono "a", "ago", ecc ma se un dato il risultato del frame è stato desiderato allora è solo una questione di as.data.frame(out).

2. Ridurre. Ecco una seconda soluzione. Esso utilizza Reduce nel nucleo del 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. Questo aggiunge i nomi alla lista e poi estrae le frequenze, i nomi e gruppi come una lunga vettoriali ogni metterli di nuovo insieme utilizzando 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

Benchmarking alcune delle soluzioni utilizzando il rbenchmark pacchetto si ottiene quanto segue che indica che la soluzione zoo è la più veloce sui dati di esempio ed è probabilmente anche la più semplice.

> 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 

AGGIUNTO: seconda soluzione.

AGGIUNTO: terza soluzione.

AGGIUNTO: benchmark.

+0

Grazie G. ma quando eseguo il tuo esempio di codice, ottengo un errore: Errore in eval (sostituto (expr), dati, enclos = parent.frame()): numerico 'envir' arg non di lunghezza uno – litlogger

+0

@litlogger, l'ho risolto. –

Problemi correlati