5 votes

Découper une chaîne de caractères à des indices consécutifs avec R / Rcpp ?

Je veux écrire une fonction qui découpe une "chaîne" en un vecteur, séquentiellement, à un index donné. J'ai une solution R assez adéquate pour cela ; cependant, je pense qu'écrire le code en C/C++ serait probablement plus rapide. Par exemple, j'aimerais pouvoir écrire une fonction "strslice" qui fonctionne comme suit :

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

Cependant, je ne sais pas comment traiter les éléments du "CharacterVector" transmis dans le code Rcpp comme des chaînes de caractères. Voici ce que j'imagine pouvoir fonctionner (étant donné mon manque de connaissances en C++/Rcpp, je suis sûr qu'il existe une meilleure approche) :

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 );
  ')

Pour mémoire, le code R correspondant que j'ai est le suivant :

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

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

}

...mais je pense que sauter autant entre les structures de données ralentira les choses avec de très grandes chaînes.

(Autre possibilité : existe-t-il un moyen de contraindre "strsplit" à se comporter comme je le souhaite ?)

7voto

Romain Francois Points 8223

J'utiliserais substring . Voici ce qu'il en est :

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

À propos de votre Rcpp vous pouvez peut-être allouer le code std::vector<std::string> avec la bonne taille, afin d'éviter de le redimensionner, ce qui pourrait nécessiter des allocations de mémoire, ... ou peut-être utiliser directement un fichier Rcpp::CharacterVector . Voici ce qu'il en est :

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"

4voto

G. Grothendieck Points 40825

Cette phrase d'une ligne utilisant strapplyc del gsubfn est suffisamment rapide pour que rcpp ne soit pas nécessaire. Ici, nous l'appliquons à l'ensemble du texte d'Ulysse de James Joyce, ce qui ne prend que quelques secondes :

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]])

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X