将频率表组合成单个数据帧

时间:2012-02-12 16:15:41

标签: r plyr

我有一个列表,其中每个列表项是在不同的示例文本上使用“table()”派生的词频表。因此,每个表的长度不同。我现在想将列表转换为单个数据框,其中每列是一个单词,每一行都是一个示例文本。以下是我的数据的虚拟示例:

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)

所以,有人得到这种结构:

> 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

我现在需要将此列表(myList)转换为单个数据框。我想我可以用plyr这样做,就像这里所做的那样(http://ryouready.wordpress.com/2009/01/23/r-combining-vectors-or-data-frames-of-unequal-长度为一个数据帧/),例如

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

但似乎我的“桌子”对象不能很好玩。我尝试将它们转换为dfs,也转换为矢量,但这些都没有用。

3 个答案:

答案 0 :(得分:7)

<强> 1。动物园即可。 zoo包具有多路合并功能,可以紧凑地完成。 lapplymyList的每个组件转换为zoo对象,然后我们将它们全部合并:

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

以上内容返回了一个多变量动物园系列,其中“时间”为"a""ago"等,但如果需要数据框结果,则只需as.data.frame(out)

<强> 2。减少即可。这是第二种解决方案。它在R的核心使用Reduce

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 即可。这个将名称添加到列表中,然后将频率,名称和组提取为一个长向量,每个向量使用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))
))

<强>基准

使用rbenchmark软件包对一些解决方案进行基准测试,我们得到以下结果,表明动物园解决方案在样本数据上是最快的,并且可以说是最简单的。

> 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

增加:第二个解决方案。

增加:第三种解决方案。

增加:基准。

答案 1 :(得分: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)

答案 2 :(得分:1)

这是一种完成工作的不雅方式。我确定那里只有一个1-liner,但我不知道在哪里:

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

再看一下,这是另一种方式,使输出更类似于链接博客文章中的输出:[编辑:现在有效]

    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)

同样难看,所以也许还会有更好的答案。