【问题标题】:Combine frequency tables into a single data frame将频率表合并到一个数据框中
【发布时间】:2012-02-12 16:15:41
【问题描述】:

我有一个列表,其中每个列表项都是一个词频表,该表源自在不同的示例文本上使用“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 和向量,但都不是很正确。

【问题讨论】:

  • 哦,等等,在我的回答中,我假设您希望每个表都有一个单独的 data.frame 列。您是否使用了不同的格式?

标签: r plyr


【解决方案1】:

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 包对一些解决方案进行基准测试,我们得到以下结果,这表明 zoo 解决方案在样本数据上是最快的,并且可以说也是最简单的。

> 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

添加:第二种解决方案。

添加:第三种解决方案。

添加:基准测试。

【讨论】:

  • 谢谢 G. 但是当我运行你的代码示例时,我得到一个错误: eval 中的错误(substitute(expr), data, enclos = parent.frame()) : numeric 'envir' arg not长度为一
【解决方案2】:
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)

【讨论】:

    【解决方案3】:

    这是完成工作的一种不优雅的方式。我敢肯定有一个 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)
    

    也很丑,所以也许还会有更好的答案。

    【讨论】:

    • 感谢蒂姆,我希望避免 for 循环,但这似乎可以完成工作。然后我可以转置 df 并进行一些修剪,使单词成为列名。 . .不过,在我看来,应该有一个基于 plyr 的解决方案。 . . .
    • @litlogger 第二种方法,仍然丑陋,现在可以工作并避免 for 循环
    • 我应该提一下,标点符号,例如"-"names(x) 中变成"",这导致rbind.fill() 出错。我把它们扔到匿名的lapply 函数中。仅供参考,以防万一
    • 蒂姆,标点符号很好。我在自己的测试中遇到了错误,我现在看到标点符号是原因。与我的直觉相反,您的第一个循环解决方案似乎比 plyr 解决方案更有效,至少当我将其应用于具有大约 55,000 个唯一词的真实数据集时!
    • @litlogger 看中了!请注意,在循环版本中,所有标点符号都被分组到""下的一行中
    猜你喜欢
    • 1970-01-01
    • 2017-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-18
    • 2019-01-07
    • 2019-06-26
    相关资源
    最近更新 更多