2016-03-26 18 views
3

これは本当に簡単な作業のようですが、ベースRで良い解決策を見つけることができません。2N文字の文字列があります。これを長さNの文字ベクトルに分割するにはどうすればよいですか?各要素は2文字の文字列ですか?文字列を固定サイズのチャンクに分割する

私はVectorizesubstrのようなものを使用することができます

vss <- Vectorize(substr, c("start", "stop")) 
ch <- paste(rep("a", 1e6), collapse="") 
vss(ch, seq(1, nchar(ch), by=2), seq(2, nchar(ch), by=2)) 

をが、これは長い文字列(O(N^2)私は信じている)のために本当に遅いです。

+2

'first'と' last'でもベクトル化された 'substring'を使用してください。 – nicola

+0

それは動作しますが、 'Vectorize(substr)'と同じ問題があります。すなわち、O(N^2)ランタイムです。それはまた、最初の文字列のN/2個のコピーを作成するので、O(N^2)メモリにもなります! –

+1

文字列の文字列がASCIIの場合(または少なくともマルチバイト文字を持たない場合)、 'apply(matrix(charToRaw(ch)、nrow = 2)、2、rawToChar)'を試してみてください'substring'よりも速く、大部分は線形にスケールします。 – nicola

答えて

2

あなたはスピードをしたい場合は、Rcppは常に良い選択です:

library(Rcpp); 
cppFunction(' 
    List strsplitN(std::vector<std::string> v, int N) { 
     if (N < 1) throw std::invalid_argument("N must be >= 1."); 
     List res(v.size()); 
     for (int i = 0; i < v.size(); ++i) { 
      int num = v[i].size()/N + (v[i].size()%N == 0 ? 0 : 1); 
      std::vector<std::string> resCur(num,std::string(N,0)); 
      for (int j = 0; j < num; ++j) resCur[j].assign(v[i].substr(j*N,N)); 
      res[i] = resCur; 
     } 
     return res; 
    } 
'); 

ch <- paste(rep('a',1e6),collapse=''); 
system.time({ res <- strsplitN(ch,2L); }); 
## user system elapsed 
## 0.109 0.015 0.121 
head(res[[1L]]); tail(res[[1L]]); 
## [1] "aa" "aa" "aa" "aa" "aa" "aa" 
## [1] "aa" "aa" "aa" "aa" "aa" "aa" 
length(res[[1L]]); 
## [1] 500000 

便利な参照:http://gallery.rcpp.org/articles/strings_with_rcpp/


もっとデモ:

1:

strsplitN(c('abcd','efgh'),2L); 
## [[1]] 
## [1] "ab" "cd" 
## 
## [[2]] 
## [1] "ef" "gh" 
## 
strsplitN(c('abcd','efgh'),3L); 
## [[1]] 
## [1] "abc" "d" 
## 
## [[2]] 
## [1] "efg" "h" 
## 
strsplitN(c('abcd','efgh'),1L); 
## [[1]] 
## [1] "a" "b" "c" "d" 
## 
## [[2]] 
## [1] "e" "f" "g" "h" 
## 
strsplitN(c('abcd','efgh'),5L); 
## [[1]] 
## [1] "abcd" 
## 
## [[2]] 
## [1] "efgh" 
## 
strsplitN(character(),5L); 
## list() 
strsplitN(c('abcd','efgh'),0L); 
## Error: N must be >= 1. 

上記の実装を持つ2つの重要な注意点がありますそれは正しくNA Sを処理しません。 Rcppはstd::stringが出てくると、'NA'に文字列化されているようです。問題のリストコンポーネントを真のNAに置き換えたラッパーを使ってRlandで簡単に解決できます。

x <- c('a',NA); strsplitN(x,1L); 
## [[1]] 
## [1] "a" 
## 
## [[2]] 
## [1] "N" "A" 
## 
x <- c('a',NA); ifelse(is.na(x),NA,strsplitN(x,1L)); 
## [[1]] 
## [1] "a" 
## 
## [[2]] 
## [1] NA 
## 

2:それは正しくマルチバイト文字を処理しません。これは厳しい問題であり、Unicode対応のトラバーサルを使用するためにコア関数の実装を書き直す必要があります。この問題を解決すると、割り当てループの前に1回のショットで各ベクトルを事前に割り当てることができないため、パフォーマンスが大幅に低下する可能性があります。

strsplitN('aΩ',1L); 
## [[1]] 
## [1] "a" "\xce" "\xa9" 
## 
strsplit('aΩ',''); 
## [[1]] 
## [1] "a" "Ω" 
## 
関連する問題