簡體   English   中英

data.frame的大字符串向量

[英]large string vector to data.frame

我有一個大型矢量(100M元素)的單詞類型:

words <- paste(letters,letters,letters,letters,sep="_")

(實際數據中的單詞並不完全相同,但全長為8)

我想將它們轉換為一個數據框,每個字母的每個字母都有一列,每個字有一行。 為此我在結果上嘗試了str_split_fixedrbind但是在大矢量R上凍結/需要永遠。

如此期望的形式輸出:

      l1    l2    l3    l4
1     a     a     a     a  
2     b     b     b     b
3     c     c     c     c

有更快的方法嗎?

解:

  • 使用paste()將向量元素折疊在一起
  • 使用fread()將折疊的向量解析為data.table / data.frame

作為一個功能:

collapse2fread <- function(x,sep) {

    require(data.table)
    fread(paste0(x,collapse="\n"),sep=sep,header=FALSE)
}

Rcpp在那之上?

也可以嘗試通過Rcpp包在c ++中實現它以獲得更多的東西嗎? 就像是:

std::string collapse_cpp(CharacterVector subject,const std::string collapseBy){

     int n = subject.size();
     std::string collapsed;

     for(int i=0;i<n;i++){
         collapsed += std::string(subject[i]) + collapseBy;
    }
    return(collapsed);
}

然后我們得到:

collapse_cpp2fread <- function(x,sep) {

    require(data.table)
    fread(collapse_cpp(x,collapse="\n"),sep=sep,header=FALSE)
}

快速測試cpp fxn

microbenchmark(
    paste0(words,collapse="\n"),
    collapse_cpp(words,"\n"),
    times=100)

不多但是有點兒:

> Unit: microseconds
>                             expr   min     lq median     uq    max neval
>  paste0(words, collapse = "\\n") 7.297 7.7695  8.162 8.4255 33.824   100
>       collapse_cpp(words, "\\n") 4.477 5.0095  5.117 5.3525 17.052   100

與strsplit方法的比較:

做一個更真實的輸入

words <- rep(paste0(letters[1:8], collapse = '_'), 1e5) # 100K elements

風向標:

microbenchmark(
    do.call(rbind, strsplit(words, '_')),
    fread(paste0(words,collapse="\n"),sep="_",header=FALSE),
    fread(collapse_cpp(words,"\n"),sep="_",header=FALSE),
    times=10)

得到:

> Unit: milliseconds
>                                                               expr       min        lq    median                  uq
>                               do.call(rbind, strsplit(words, "_")) 782.71782 796.19154 822.73694 854.22211
> fread(paste0(words, collapse = "\\n"), sep = "_", header = FALSE)  62.56164  64.13504  68.22512  71.96075
> fread(collapse_cpp(words, "\\n"), sep = "_", header = FALSE)  47.16362  47.78030  50.12867  52.23102
>      max neval
> 863.0790    10
> 151.5969    10
> 109.9770    10

這么大約20倍的改進? 希望能幫助到你!

擴展基於Rcpp的解決方案。 如果你可以假定輸入的結構,那么很容易在Rcpp中完成所有這一切,只需要最少的數據復制。

// [[Rcpp::export]]
List bazinga( CharacterVector txt, int nc ){
    int n = txt.size() ;

    std::vector<CharacterVector> columns(nc) ;
    for( int i=0; i<nc; i++){
        columns[i] = CharacterVector(n) ;    
    }

    std::string tmp ;
    for( int i=0; i<n; i++){
        const char* p = txt[i];
        for(int j=0; j<nc; j++){
            tmp = *p ;
            columns[j][i] = tmp ;
            p +=2 ;
        }
    }

    List out = wrap(columns) ;
    return out ;
}

我明白了:

> microbenchmark(f(), bazinga(words, 8), collapse2fread(words,
+     "_"), collapse_cpp2fread(words, "_"), times = 10)
Unit: milliseconds
                           expr       min        lq    median         uq          max neval
                            f() 830.21571 871.38955 899.07207 1001.18561   1299.15783    10
              bazinga(words, 8)  26.26454  30.61620  33.37360   46.24160     64.09243    10
     collapse2fread(words, "_")  59.96217  61.58535  67.20007   93.61615     97.85007    10
 collapse_cpp2fread(words, "_")  46.79471  48.58391  49.99636   82.69684    119.88587    10

如果您使用的是Unix,那么您應該利用命令行。 在那里處理大數據通常會更快,然后在減少后將其帶入R中。 在這里,我將words vector寫入文件,然后在system R函數中使用Unix命令重寫它。

> words <- rep(paste0(letters[1:8], collapse = '_'), 1e5)

> cat(words, file = 'out.txt', sep = '\n')
> write.table(system(' cat out.txt | tr "_" " " ', intern = TRUE),
              row.names = FALSE, col.names = FALSE, 
              quote = FALSE, file = 'out.txt')

> head(read.table('out.txt'))
#   V1 V2 V3 V4 V5 V6 V7 V8
# 1  a  b  c  d  e  f  g  h
# 2  a  b  c  d  e  f  g  h
# 3  a  b  c  d  e  f  g  h
# 4  a  b  c  d  e  f  g  h
# 5  a  b  c  d  e  f  g  h
# 6  a  b  c  d  e  f  g  h

和典型的R do.call(rbind, ...)方法:

f <- function()
{
    x <- do.call(rbind, strsplit(words, '_'))
    y <- data.frame(x)
    names(y) <- paste0('l', ncol(y))
    return(y)
}

> microbenchmark(f())
# Unit: milliseconds
#  expr      min      lq   median      uq      max neval
#   f() 818.2391 959.088 964.1105 989.081 997.8625   100

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM