【问题标题】:How to vectorize R strsplit?如何矢量化 R strsplit?
【发布时间】:2010-06-16 15:14:31
【问题描述】:

创建使用strsplit 的函数时,向量输入的行为不符合预期,需要使用sapply。这是由于strsplit 产生的列表输出。有没有办法对过程进行矢量化 - 即函数为输入的每个元素在列表中生成正确的元素?

例如,计算字符向量中单词的长度:

words <- c("a","quick","brown","fox")

> length(strsplit(words,""))
[1] 4 # The number of words (length of the list)

> length(strsplit(words,"")[[1]])
[1] 1 # The length of the first word only

> sapply(words,function (x) length(strsplit(x,"")[[1]]))
a quick brown   fox 
1     5     5     3 
# Success, but potentially very slow

理想情况下,length(strsplit(words,"")[[.]]) 之类的东西,其中. 被解释为输入向量的相关部分。

【问题讨论】:

    标签: r vectorization strsplit


    【解决方案1】:

    一般来说,您应该尝试使用矢量化函数开始。使用strsplit 之后会经常需要某种迭代(这会更慢),所以尽量避免它。在您的示例中,您应该改用nchar

    > nchar(words)
    [1] 1 5 5 3
    

    更一般地说,利用strsplit 返回一个列表的事实并使用lapply

    > as.numeric(lapply(strsplit(words,""), length))
    [1] 1 5 5 3
    

    或者使用来自plyrl*ply 系列函数。例如:

    > laply(strsplit(words,""), length)
    [1] 1 5 5 3
    

    编辑:

    为了纪念Bloomsday,我决定使用 Joyce 的 Ulysses 来测试这些方法的性能:

    joyce <- readLines("http://www.gutenberg.org/files/4300/4300-8.txt")
    joyce <- unlist(strsplit(joyce, " "))
    

    现在我已经掌握了所有单词,我们可以数数了:

    > # original version
    > system.time(print(summary(sapply(joyce, function (x) length(strsplit(x,"")[[1]])))))
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      0.000   3.000   4.000   4.666   6.000  69.000 
       user  system elapsed 
       2.65    0.03    2.73 
    > # vectorized function
    > system.time(print(summary(nchar(joyce))))
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      0.000   3.000   4.000   4.666   6.000  69.000 
       user  system elapsed 
       0.05    0.00    0.04 
    > # with lapply
    > system.time(print(summary(as.numeric(lapply(strsplit(joyce,""), length)))))
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      0.000   3.000   4.000   4.666   6.000  69.000 
       user  system elapsed 
        0.8     0.0     0.8 
    > # with laply (from plyr)
    > system.time(print(summary(laply(strsplit(joyce,""), length))))
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      0.000   3.000   4.000   4.666   6.000  69.000 
       user  system elapsed 
      17.20    0.05   17.30
    > # with ldply (from plyr)
    > system.time(print(summary(ldply(strsplit(joyce,""), length))))
           V1        
     Min.   : 0.000  
     1st Qu.: 3.000  
     Median : 4.000  
     Mean   : 4.666  
     3rd Qu.: 6.000  
     Max.   :69.000  
       user  system elapsed 
       7.97    0.00    8.03 
    

    矢量化函数和lapply 比原始sapply 版本快得多。所有解决方案都返回相同的答案(如摘要输出所示)。

    显然plyr 的最新版本更快(这是使用稍旧的版本)。

    【讨论】:

    • 感谢 Shane,但我所做的事情并没有得到相同的结果。它是 Verhoeff 校验位方案的实现。我已经修改了我的函数以与上述实现兼容,但是输入一个 100,000 长的向量,我从第一个得到一个 8 个元素的列表,从第二个得到一个 8 个元素的向量(8 个是最向量元素的可能长度)。
    • @James:那么我想你的函数肯定有其他事情发生。正如您在上面看到的,我刚刚在一个包含超过 270k 记录的向量上进行了测试,并从每个记录中得到了相同的结果。您可以尝试提供更多代码或提供一些数据。
    • 顺便说一句,我刚刚在 R 2.11.1 中安装了 plyr 版本 0.1.9,并且与上面的时间相似。
    • @Shane:是的,当我调用它时,我错误地索引了列表。它现在可以工作了,但 lapply 的时间并不比 sapply 好多少。该算法需要按顺序处理拆分数字,所以这可能是导致问题的原因。
    • 在开发版本中修复了 plyr 的缓慢性 - 但在处理单个应用程序的时间占主导地位的更复杂的问题时,plyr 通常更有用。
    猜你喜欢
    • 2016-05-30
    • 1970-01-01
    • 2020-11-18
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多