代码之家  ›  专栏  ›  技术社区  ›  user1357015

在非常大的数据帧上运行“apply”命令

  •  1
  • user1357015  · 技术社区  · 7 年前

    在R中有一个tibble,其维数为 15,000,000 x 140 .就大小而言,大约为6 gb。

    我想检查给定行的第11-40列中的任何一列是否从特定列表开始。我想得到一个1的向量&0的长度为15000000。

    我可以使用以下方法执行此操作:

    subResult <- apply(rawData[,11:40], c(1,2), function(x){substring(x,1,3) %in% c("295", "296", "297", "298", "299")})
    
    result <- apply(subResult, 1, sum)
    

    问题是,这太慢了——仅仅第一条线就需要一天多的时间。

    有没有什么方法可以更快地做到这一点——可能直接通过dplyr或数据。桌子

    非常感谢。

    这里是一个数据采样,只保留到第11-40列。

    !> head(rawData)
     # A tibble: 6 x 30                                                                                                                                                                               
       X1    X2    X3    X4    X5    X6    X7    X8    X9    X10   X11   X12   X13
       <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
     1 39402 39451 3fv3i 19593 fk20 14p4  59304  329fj2 NA    NA    NA    NA    NA
     2 39422 f203ff vmio2  vo2493  19149 59833 13404 394034 43920  349304   59302 1934 34834
     3 3432f32 fe493  43943 H2344 53049  V602  3124  K148 K13  NA    NA    NA    NA
     # ... with 17 more variables: X14 <chr>, X15 <chr>, X16 <chr>, X17 <chr>,                                                                                                                         
     #   X18 <chr>, X19 <chr>, X20 <chr>, X21 <chr>, X22 <chr>, X23 <chr>,                                                                                                                             
     #   X24 <chr>, X25 <chr>, X26 <chr>, X27 <chr>, X28 <chr>, X29 <chr>, X30 <chr> 
    
    3 回复  |  直到 7 年前
        1
  •  3
  •   F. Privé    7 年前

    我的意见:

    • 应用将数据转换为矩阵
    • 数据框首先是一个列表,而不是一个矩阵
    • substring() 是矢量化函数( %in% 也是)

    因此,我会:

    sapply(rawData[11:40], function(var) {
      substring(var, 1, 3) %in% c("295", "296", "297", "298", "299")
    })
    

    然后使用 rowSums() 而不是 apply(subResult, 1, sum)

        2
  •  2
  •   akrun    7 年前

    根据描述,可以使用 tidyverse

    library(tidyverse)
    rawData %>%
       select(11:40) %>% #select the columns
       #convert to logical columns
       mutate_all(funs(substring(.,1,3) %in% c("295", "296", "297", "298", "299"))) %>% 
       reduce('+') %>% #get the rowwise sum
       mutate(rawData, newcol = .) # assign a new column to the original data
    

    或与 data.table 通过转换“数据”。帧“到”数据。表'( setDT(rawData) ),指定中感兴趣的列 .SDcols ,循环遍历列,使用OP的条件将其转换为逻辑, Reduce 通过采取 sum 并指定( := )至“newcol”

    library(data.table)
    setDT(rawData)[, newCol := Reduce('+', lapply(.SD, function(x) 
          substring(x, 1, 3) %chin% c("295", "296", "297", "298", "299"))), 
         .SDcols = 11:40]
    
        3
  •  2
  •   Paweł Kozielski-Romaneczko    7 年前

    尝试使用 Rcpp 包裹

    这是一个简单的C++程序,它接受两个字符串向量,并检查第一个元素中的3个字符是否等于第二个。因此,它将输出大小长度(第一个向量)x长度(第二个向量)的逻辑矩阵。

    #include <Rcpp.h>
    using namespace Rcpp;
    
    // [[Rcpp::export]]
    LogicalMatrix IndicatorMatrix(std::vector<std::string> target, std::vector<std::string> tocheck) {
    
      int nrows = target.size();
      int ncols = tocheck.size();
    
      LogicalMatrix ind(nrows, ncols);
    
      for(int r=0; r<nrows; r++) {
        for(int c=0; c<ncols; c++) {
    
          bool found = target[r].substr(0,3) == tocheck[c];
          ind(r,c) = found;
    
        }
      }
    
      return ind;
    
    }
    

    之后,您可以将此程序源代码转换为R并使用 IndicatorMatrix 函数,就像它将是一个R函数对象一样。

    library(Rcpp)
    sourceCpp("C:/Users/Desktop/indicatorMatrix.cpp")
    
    rep("123456", 15000000) -> x
    df <- data.frame(x,x,x,x,x,x,x,x, stringsAsFactors=FALSE)
    y <- c("123", "124", "345", "231", "675", "344", "222")
    
    
    t1 <- Sys.time()
    out <- lapply(1:length(df), function(col) {
    
      res <- IndicatorMatrix(unlist(df[,col]), y)
      res
    
    })
    t2 <- Sys.time()
    t2-t1
    

    程序在大约100秒内用1500万行在8列数据帧中搜索8个3字符字符串。所以这可能是你的正确方向。