Разрезать строку по последовательным индексам с помощью R/Rcpp?

Я хочу написать функцию, которая последовательно нарезает «строку» на вектор по заданному индексу. У меня есть для этого достаточно адекватное решение R; однако я полагаю, что написание кода на C/C++, вероятно, будет быстрее. Например, я хотел бы иметь возможность написать функцию «strslice», которая работает следующим образом:

x <- "abcdef"
strslice( x, 2 ) ## should return c("ab", "cd", "ef")

Однако я не уверен, как обрабатывать элементы «CharacterVector», передаваемые в коде Rcpp, как строки. Это то, что я думаю, может сработать (учитывая отсутствие у меня знаний С++/Rcpp, я уверен, что есть лучший подход):

f <- rcpp( signature(x="character", n="integer"), '
  std::string myString = Rcpp::as<std::string>(x);
  int cutpoint = Rcpp::as<int>(n);
  vector<std::string> outString;
  int len = myString.length();
  for( int i=0; i<len/n; i=i+n ) {
    outString.push_back( myString.substr(i,i+n-1 ) );
    myString = myString.substr(i+n, len-i*n);
  }
  return Rcpp::wrap<Rcpp::CharacterVector>( outString );
  ')

Для справки, соответствующий R-код у меня есть:

strslice <- function(x, n) {
  x <- as.data.frame( stringsAsFactors=FALSE, 
                      matrix( unlist( strsplit( x, "" ) ), ncol=n, byrow=T )
  )

  do.call( function(...) { paste(..., sep="") }, x )

}

... но я полагаю, что прыжки между структурами данных так сильно замедлят работу с очень большими строками.

(В качестве альтернативы: есть ли способ заставить «strsplit» вести себя так, как я хочу?)


person Kevin Ushey    schedule 10.11.2012    source источник
comment
Вероятно, вам следует взглянуть на пакет Biostrings.   -  person IRTFM    schedule 10.11.2012


Ответы (2)


Я бы использовал substring. Что-то вроде этого:

strslice <- function( x, n ){   
    starts <- seq( 1L, nchar(x), by = n )
    substring( x, starts, starts + n-1L )
}
strslice( "abcdef", 2 )
# [1] "ab" "cd" "ef"

Что касается вашего кода Rcpp, возможно, вы можете выделить std::vector<std::string> с правильным размером, чтобы избежать изменения его размера, что может означать выделение памяти, ... или, возможно, напрямую использовать Rcpp::CharacterVector. Что-то вроде этого:

strslice_rcpp <- rcpp( signature(x="character", n="integer"), '
    std::string myString = as<std::string>(x);
    int cutpoint = as<int>(n);
    int len = myString.length();
    int nout = len / cutpoint ;
    CharacterVector out( nout ) ;
    for( int i=0; i<nout; i++ ) {
      out[i] = myString.substr( cutpoint*i, 2 ) ;
    }
    return out ;
')
strslice_rcpp( "abdcefg", 2 )
# [1] "ab" "cd" "ef"
person Romain Francois    schedule 10.11.2012
comment
Это решение Rcpp молниеносно. Спасибо! - person Kevin Ushey; 11.11.2012

Этот однострочный код с использованием strapplyc из пакета gsubfn работает достаточно быстро, поэтому rcpp может не понадобиться. Здесь мы применяем его ко всему тексту «Улисса» Джеймса Джойса, что занимает всего несколько секунд:

library(gsubfn)
joyce <- readLines("http://www.gutenberg.org/files/4300/4300-8.txt") 
joycec <- paste(joyce, collapse = " ") # all in one string 
n <- 2
system.time(s <- strapplyc(joycec, paste(rep(".", n), collapse = ""))[[1]])
person G. Grothendieck    schedule 10.11.2012