我有一个大型数据框(> 400万行),其中包含存储字符串的列 yname1
, yname2
, yname3
:
yname1 | yname2 | yname3
aaaaaa | bbbaaa | bbaaaa
aaabbb | cccccc | aaaaaa
aaaaaa | aaabbb | dddddd
cccccc | dddddd | eeeeee
现在我想计算所有列中每个字符串的出现总次数 . 这些应作为附加列添加:
yname1 | yname2 | yname3 | rcount1 | rcount2 | rcount3
aaaaaa | bbbaaa | bbaaaa | 3 | 1 | 1
aaabbb | cccccc | aaaaaa | 2 | 2 | 3
aaaaaa | aaabbb | dddddd | 3 | 2 | 2
cccccc | dddddd | eeeeee | 2 | 2 | 1
我已经编写了以下代码,它完成了这项工作:
data3$rcount1 <- sapply(data3$yname1, function(x) sum(data2$yname1==x)+sum(data2$yname2==x)+sum(data2$yname3==x))
data3$rcount2 <- sapply(data3$yname2, function(x) sum(data2$yname1==x)+sum(data2$yname2==x)+sum(data2$yname3==x))
data3$rcount3 <- sapply(data3$yname3, function(x) sum(data2$yname1==x)+sum(data2$yname2==x)+sum(data2$yname3==x))
但是,这确实很慢,需要花费数天才能计算出来 . 我有什么想法可以加快速度吗?
5 回答
data.table
方法怎么样:Microbenchmark输出复制来自bgoldst的例子,但有400,000行 .
在基础R中,您可以构建一个包含data.frame的未列出值的表,并按值对其进行索引 . 确保你索引的是一个字符串,而不是一个因子(因此是
as.character
),或者它将被数字而不是名称索引 .如果data.frame足够大以至于速度很慢,那么您可以在
lapply
之外构建表,因此它只运行一次:你也可以把它放在
dplyr
中,这使它更具可读性:数据
已经有一些很好的解决方案,但没有人使用
match()
在预先计算的频率表中查找每个字符串 . 以下是如何做到这一点 . 请注意,我选择as.matrix()
为table()
的参数和match()
的第一个参数生成yname*
列的矩阵 .Update: 我不敢相信我以前错过了这个,但表达方式
可以替换为
因此根本不需要调用
match()
.我只是重新评估基准测试,发现它并没有以任何显着的方式改变我的解决方案的运行时间(可能只是在小规模测试中略微加速) . 推测这是因为索引带有字符名称的向量需要内部使用相同类型的
match()
逻辑,因此上述替换不会获得任何性能 . 但我会说简洁和简洁的改进是值得的 .基准测试
我应该注意到,我对其他一些解决方案进行了一些小的修改,以便产生这些基准测试结果 . 最值得注意的是,我想避免为重复执行复制任何输入,但由于data.tables通过引用传递,我不得不修改
jota()
以使其成为幂等的 . 这只涉及目标yname*
列的过滤,我通过grep()
调用预先计算到一个名为cns
的局部变量,就像我在自己的解决方案中一样 . 为了公平起见,我向所有解决方案添加了相同的grep()
调用和过滤逻辑,但markus()
除外,它不需要它,因为它分别显式处理每一列 . 我还将jota()
中的lookup
上的索引连接操作更改为lookup[.(value=x),,on='value']
,因为否则它不适用于我 . 最后,对于mhairi()
,我通过在所有yname*
列中添加Reduce()
调用来完成解决方案 .我更喜欢上面的答案,但为了完整性,让我添加一个替代方案,它基于使用唯一字符串作为rownames:
现在我们有一个数据帧,其中包含唯一字符向量的出现次数,字符向量是rownames . 我们可以使用这些来表示所述数据帧 .
Edit:
看看其他答案,并且考虑到我们开始谈论性能,我意识到上面的内容是不必要的复杂,可以改进如下:
这样可以避免完全调用
reshape2
和dplyr
,并相应地提高性能 . 运用这个解决方案现在要快得多,但速度不如某些替代方案快 . 看到
我认为找到每个唯一值的总和然后加入原始表会更快 .
并为每一行重复合并 .