【问题标题】:Converting R loops to functional form with apply使用 apply 将 R 循环转换为函数形式
【发布时间】:2013-11-03 19:32:34
【问题描述】:

我编写了一些 R 代码来解析字符串,计算子字符串的出现次数,然后填充子字符串计数表。它工作正常,但在我使用的实际数据(非常大)上真的很慢,而且我知道其中很多是因为我使用的是循环而不是 apply 系列中的函数。我一直在尝试将此代码转换为功能形式,但我没有任何运气,有人可以帮忙吗?我最大的问题是我想不出一种方法来使用列名来匹配 apply 构造中的值。这是包含一些玩具数据的代码:

#Create toy data, list of unique substrings
code_frame<-matrix(c(c('a|a|b|c|d'),c('a|b|b|c|c'),c('a|b|c|d|d')),nrow=3,ncol=1)   
all_codes_list<-c('a','b','c','d')

#create data frame with a column for each code and a row for each job
code_count<-as.data.frame(matrix(0, ncol = length(all_codes_list), nrow = nrow(code_frame)))
colnames(code_count)<-all_codes_list

#fill in the code_count data frame with entries where codes occur
for(i in 1:nrow(code_frame)){
    test_string<-strsplit(code_frame[i,1],split="|",fixed=TRUE)[[1]]
    for(j in test_string){
        for(g in 1:ncol(code_count)){
            if(j == all_codes_list[g]){
                code_count[i,g]<-code_count[i,g]+1
                }
            }
        }
    }

谢谢。

【问题讨论】:

  • 这真的是 code_frame 的样子吗?你如何回答将取决于我给出的建议..
  • 是的,基本上。这是一个以管道分隔的长字符串,我需要将其分解为子字符串。这不是它的创建方式(它已经从我从中提取它的数据库中形成了一个长管道 delim str ),但形式是相同的。

标签: r functional-programming apply


【解决方案1】:

qdap package 有一个工具可以完美解决这个问题,它应该非常快速且编码很少,称为 mtabulate

library(qdap)    
mtabulate(strsplit(code_frame, "\\|"))

##   a b c d
## 1 2 1 1 1
## 2 1 2 2 0
## 3 1 1 1 2

基本上,它采用向量列表(从strsplit 输出)并为每个向量制作一行列表信息。

编辑: 如果速度真的很重要,这里是 1000 次复制的基准(microbenchmark package 在 Win 7 机器上):

Unit: microseconds
     expr      min       lq   median       uq      max neval
   HONG()  592.458  620.448  632.111  644.706 4650.560  1000
  TYLER()  324.220  342.413  351.743  361.073 3556.613  1000
 HENRIK() 1527.329 1560.450 1578.177 1614.331 4828.297  1000

和视觉输出:

【讨论】:

  • 感谢您的mtabulate 功能和时间,+1!
【解决方案2】:

base 替代方案:

df <- read.table(text = code_frame, sep = "|")

tt <- apply(df, 1, function(x){
  x2 <- factor(x, levels = letters[1:4])
  table(x2)
  })

t(tt) 

#      a b c d
# [1,] 2 1 1 1
# [2,] 1 2 2 0
# [3,] 1 1 1 2

【讨论】:

    【解决方案3】:

    一个单行,分成 3 行:

    do.call(rbind,
            lapply(strsplit(code_frame[,1], "|", fixed=TRUE),
                   function(x) table(factor(x, levels=all_codes_list))))
    

    请注意,strsplit 是矢量化的,因此您不需要对所有行进行外部循环。您的内部循环基本上是计算每个代码的出现次数,这是table 的应用程序。最后,do.call(rbind, *) 是将行列表转换为单个数据框的标准惯用语。

    【讨论】:

    • 这很酷。感谢所有的回复,我一定会记住“表格”功能。
    猜你喜欢
    • 1970-01-01
    • 2017-06-02
    • 2020-11-02
    • 2016-06-12
    • 1970-01-01
    • 2022-12-03
    • 2020-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多