如果你想要速度,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); });
head(res[[1L]]); tail(res[[1L]]);
length(res[[1L]]);
有用的参考资料:http://gallery.rcpp.org/articles/strings_with_rcpp/。
更多示例:
strsplitN(c('abcd','efgh'),2L)
strsplitN(c('abcd','efgh'),3L)
strsplitN(c('abcd','efgh'),1L)
strsplitN(c('abcd','efgh'),5L)
strsplitN(character(),5L)
strsplitN(c('abcd','efgh'),0L)
以上实现有两个重要的注意事项:
1:它不能正确处理NA
。当Rcpp被强制生成std::string
时,似乎会将其字符串化为'NA'
。您可以在R中使用包装器轻松解决此问题,用真正的NA
替换有问题的列表组件。
x <- c('a',NA); strsplitN(x,1L);
x <- c('a',NA); ifelse(is.na(x),NA,strsplitN(x,1L));
2: 它不能正确处理多字节字符。这是一个更棘手的问题,需要重写核心函数实现以使用 Unicode 意识遍历。修复此问题还会产生显着的性能损失,因为您将无法在赋值循环之前一次性预分配每个向量。
strsplitN('aΩ',1L);
strsplit('aΩ','');
substring
,其中包括向量化的first
和last
。 - nicolaVectorize(substr)
一样存在 O(N^2) 的运行时间问题。此外,它还会复制 N/2 次初始字符串,因此也需要 O(N^2) 的内存! - Hong Ooiapply(matrix(charToRaw(ch),nrow=2),2,rawToChar)
,它似乎比substring
快得多,并且基本上是线性扩展的。 - nicola