2010-02-11 10 views
43

tengo un objeto que contiene una cadena de texto:Picar una cadena en un vector de elementos de caracteres de ancho fijo

x <- "xxyyxyxy" 

y quiero a su vez que en un vector con cada elemento que contiene dos letras:

[1] "xx" "yy" "xy" "xy" 

parece que strsplit() debería ser mi ticket, pero como no tengo foo de expresión regular, no puedo encontrar la manera de hacer que esta función corte la cadena como yo quiero. ¿Cómo debería hacer esto?

+0

por lo que desea dividir la cadena en intervalos basados ​​en un recuento conocido, strsplit() funciona en las cuerdas fijas o reg exps, pero se parece que usted quiere que se haga por la longitud? – Dan

+0

eso es exactamente correcto. Quiero hacerlo basado en la longitud. strsplit quiere hacer coincidir una expresión regular para el delimitador y no tengo un delimitador. –

+1

Hay una respuesta mucho más rápida en stackoverflow.com dos años después. [http://stackoverflow.com/a/11619681/168976](http://stackoverflow.com/a/11619681/168976). – wind

Respuesta

52

Usando subcadena es el mejor enfoque:

substring(x, seq(1,nchar(x),2), seq(2,nchar(x),2)) 

Pero aquí es una solución con plyr:

library("plyr") 
laply(seq(1,nchar(x),2), function(i) substr(x, i, i+1)) 
+6

Solo agregamos para la generalidad que si quisiéramos todos los caracteres 'n' en lugar de cada 2, sería:' subcadena (x, seq (1, nchar (x), n), seq (n, nchar (x), n)) ' – MichaelChirico

6

corte total, JD, pero se las trae

x <- "xxyyxyxy" 
c<-strsplit(x,"")[[1]] 
sapply(seq(2,nchar(x),by=2),function(y) paste(c[y-1],c[y],sep="")) 
[1] "xx" "yy" "xy" "xy" 
+0

Ese es exactamente el truco que estaba programando. por supuesto, iba a hacer un bucle en lugar de sapply;) –

6

Ésta es una forma, pero que no utilizan regexen:

a <- "xxyyxyxy" 
n <- 2 
sapply(seq(1,nchar(a),by=n), function(x) substr(a, x, x+n-1)) 
10

strsplit va a ser problemático, mira a una expresión regular como este

strsplit(z, '[[:alnum:]]{2}') 

se dividirá en los puntos correctos, pero no queda nada.

Usted podría utilizar subcadena & amigos

z <- 'xxyyxyxy' 
idx <- 1:nchar(z) 
odds <- idx[(idx %% 2) == 1] 
evens <- idx[(idx %% 2) == 0] 
substring(z, odds, evens) 
+0

que es una forma dulce de hacerlo también. Creo que me dejé enganchar mentalmente en srtsplit() por lo cerca que strsplit (x, "") es a lo que quiero. –

+0

¿Cómo funcionaría la subcadena si tuviera que cortar la cadena después de 3 caracteres? parece que solo funcionará para chuletas de 2 caracteres. – MySchizoBuddy

19

¿Qué tal

strsplit(gsub("([[:alnum:]]{2})", "\\1 ", x), " ")[[1]] 

Básicamente, añadir un separador (aquí " ") y continuación uso strsplit

18

Aquí es una solución rápida que divide la cadena en caracteres, a continuación, pastas juntos los elementos pares y los elementos impares.

x <- "xxyyxyxy" 
sst <- strsplit(x, "")[[1]] 
paste0(sst[c(TRUE, FALSE)], sst[c(FALSE, TRUE)]) 

Configuración Benchmark:

library(microbenchmark) 

GSee <- function(x) { 
    sst <- strsplit(x, "")[[1]] 
    paste0(sst[c(TRUE, FALSE)], sst[c(FALSE, TRUE)]) 
} 

Shane1 <- function(x) { 
    substring(x, seq(1,nchar(x),2), seq(2,nchar(x),2)) 
} 

library("plyr") 
Shane2 <- function(x) { 
    laply(seq(1,nchar(x),2), function(i) substr(x, i, i+1)) 
} 

seth <- function(x) { 
    strsplit(gsub("([[:alnum:]]{2})", "\\1 ", x), " ")[[1]] 
} 

geoffjentry <- function(x) { 
    idx <- 1:nchar(x) 
    odds <- idx[(idx %% 2) == 1] 
    evens <- idx[(idx %% 2) == 0] 
    substring(x, odds, evens) 
} 

drewconway <- function(x) { 
    c<-strsplit(x,"")[[1]] 
    sapply(seq(2,nchar(x),by=2),function(y) paste(c[y-1],c[y],sep="")) 
} 

KenWilliams <- function(x) { 
    n <- 2 
    sapply(seq(1,nchar(x),by=n), function(xx) substr(x, xx, xx+n-1)) 
} 

RichardScriven <- function(x) { 
    regmatches(x, gregexpr("(.{2})", x))[[1]] 
} 

Benchmark 1:

x <- "xxyyxyxy" 

microbenchmark(
    GSee(x), 
    Shane1(x), 
    Shane2(x), 
    seth(x), 
    geoffjentry(x), 
    drewconway(x), 
    KenWilliams(x), 
    RichardScriven(x) 
) 

# Unit: microseconds 
#    expr  min  lq median  uq  max neval 
#   GSee(x) 8.032 12.7460 13.4800 14.1430 17.600 100 
#   Shane1(x) 74.520 80.0025 84.8210 88.1385 102.246 100 
#   Shane2(x) 1271.156 1288.7185 1316.6205 1358.5220 3839.300 100 
#   seth(x) 36.318 43.3710 45.3270 47.5960 67.536 100 
#  geoffjentry(x) 9.150 13.5500 15.3655 16.3080 41.066 100 
#  drewconway(x) 92.329 98.1255 102.2115 105.6335 115.027 100 
#  KenWilliams(x) 77.802 83.0395 87.4400 92.1540 163.705 100 
# RichardScriven(x) 55.034 63.1360 65.7545 68.4785 108.043 100 

Benchmark 2:

Ahora, wi Datos más grandes

x <- paste(sample(c("xx", "yy", "xy"), 1e5, replace=TRUE), collapse="") 

microbenchmark(
    GSee(x), 
    Shane1(x), 
    Shane2(x), 
    seth(x), 
    geoffjentry(x), 
    drewconway(x), 
    KenWilliams(x), 
    RichardScriven(x), 
    times=3 
) 

# Unit: milliseconds 
#    expr   min   lq  median   uq   max neval 
#   GSee(x) 29.029226 31.3162690 33.603312 35.7046155 37.805919  3 
#   Shane1(x) 11754.522290 11866.0042600 11977.486230 12065.3277955 12153.169361  3 
#   Shane2(x) 13246.723591 13279.2927180 13311.861845 13371.2202695 13430.578694  3 
#   seth(x) 86.668439 89.6322615 92.596084 92.8162885 93.036493  3 
#  geoffjentry(x) 11670.845728 11681.3830375 11691.920347 11965.3890110 12238.857675  3 
#  drewconway(x) 384.863713 438.7293075 492.594902 515.5538020 538.512702  3 
#  KenWilliams(x) 12213.514508 12277.5285215 12341.542535 12403.2315015 12464.920468  3 
# RichardScriven(x) 11549.934241 11730.5723030 11911.210365 11989.4930080 12067.775651  3 
2

la atención con subcadena, si la longitud de cadena no es un múltiplo de la longitud requerida, entonces se necesita un + (n-1) en la segunda secuencia:

substring(x,seq(1,nchar(x),n),seq(n,nchar(x)+n-1,n)) 
+0

¡Eres un genio! Usé 'x <- paste0 (x, strrep (" ", n - (nchar (x) %% n)))', ¡pero esto es mucho más conveniente! –

2

Una función auxiliar:

fixed_split <- function(text, n) { 
    strsplit(text, paste0("(?<=.{",n,"})"), perl=TRUE) 
} 

fixed_split(x, 2) 
[[1]] 
[1] "xx" "yy" "xy" "xy" 
1

Bueno, utiliza el siguiente pseudo-código para cumplir con esta tarea:

  1. Inserte una secuencia especial en cada trozo de longitud n.
  2. Divida la cuerda por dicha secuencia.

En el código, lo hice

chopS <- function(text, chunk_len = 2, seqn) 
{ 
    # Specify select and replace patterns 
    insert <- paste("(.{",chunk_len,"})", sep = "") 
    replace <- paste("\\1", seqn, sep = "") 

    # Insert sequence with replaced pattern, then split by the sequence 
    interp_text <- gsub(pattern, replace, text) 
    strsplit(interp_text, seqn) 
} 

Esto devuelve una lista con el vector de división en el interior, sin embargo, no es un vector.

0

Aquí hay una opción que usa stringi::stri_sub(). Proveedores:

x <- "xxyyxyxy" 
stringi::stri_sub(x, seq(1, stringi::stri_length(x), by = 2), length = 2) 
# [1] "xx" "yy" "xy" "xy" 
Cuestiones relacionadas