تقطيع سلسلة في متجه من عناصر حرف العرض الثابت

StackOverflow https://stackoverflow.com/questions/2247045

  •  20-09-2019
  •  | 
  •  

سؤال

لدي كائن يحتوي على سلسلة نصية:

x <- "xxyyxyxy"

وأريد تقسيم ذلك إلى متجه مع كل عنصر يحتوي على حرفين:

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

يبدو وكأنه strsplit يجب أن تكون تذكرتي، ولكن نظرا لعدم وجود تعبير منتظم فو، لا يمكنني معرفة كيفية جعل هذه الوظيفة تقطع السلسلة إلى قطع بالطريقة التي أريدها. كيف أفعل ذلك؟

هل كانت مفيدة؟

المحلول

استخدام substring هو أفضل نهج:

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

ولكن هنا حل مع plyr:

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

نصائح أخرى

فيما يلي حل سريع ينقسم السلسلة إلى أحرف، ثم يلصقان معا العناصر حتى العناصر الفردية.

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

إعداد معيار:

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

المعيار 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

المعيار 2:

الآن، مع بيانات أكبر.

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

ماذا عن

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

أساسا، إضافة فاصل (هنا ") و من ثم استعمال strsplit

StrSplit سيكون مشكلة، انظر إلى Regexp مثل هذا

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

سوف ينقسم في النقاط الصحيحة ولكن لم يتم ترك أي شيء.

يمكنك استخدام Substring & Friends

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

إليك طريقة واحدة، ولكن لا تستخدم Regexen:

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

مجموع الاختراق، دينار، لكنه يحصل عليه

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"

وظيفة المساعد:

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

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

الانتباه مع السلسلة الفرعية، إذا كان طول السلسلة ليس مضاعفا طولك المطلوب، فستحتاج إلى + (N-1) في التسلسل الثاني:

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

حسنا، استخدمت التعليمات الزائفة التالية لتلبية هذه المهمة:

  1. إدراج تسلسل خاص في كل قطعة من الطول ن.
  2. انقسام السلسلة عن طريق التسلسل المذكور.

في الكود، فعلت

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

هذا إرجاع قائمة مع متجه الانقسام في الداخل، على الرغم من عدم وجود ناقلات.

باستخدام C ++ واحد يمكن أن يكون أسرع. بالمقارنه مع نسخة GSEE.:

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

rstub <- Rcpp::cppFunction( code = '
CharacterVector strsplit2(const std::string& hex) {
  unsigned int length = hex.length()/2;
  CharacterVector res(length);
  for (unsigned int i = 0; i < length; ++i) {
    res(i) = hex.substr(2*i, 2);
  }
  return res;
}')

x <- "xxyyxyxy"
all.equal(GSee(x), rstub(x))
#> [1] TRUE
microbenchmark::microbenchmark(GSee(x), rstub(x))
#> Unit: microseconds
#>      expr   min     lq      mean median     uq       max neval
#>   GSee(x) 4.272 4.4575  41.74284 4.5855 4.7105  3702.289   100
#>  rstub(x) 1.710 1.8990 139.40519 2.0665 2.1250 13722.075   100

set.seed(42)
x <- paste(sample(c("xx", "yy", "xy"), 1e5, replace = TRUE), collapse = "")
all.equal(GSee(x), rstub(x))
#> [1] TRUE
microbenchmark::microbenchmark(GSee(x), rstub(x))
#> Unit: milliseconds
#>      expr       min        lq      mean    median       uq       max neval
#>   GSee(x) 17.931801 18.431504 19.282877 18.738836 19.47943 27.191390   100
#>  rstub(x)  3.197587  3.261109  3.404973  3.341099  3.45852  4.872195   100

من الاختبارات الخاصة بي، يكون الكود أدناه أسرع من الأساليب السابقة التي تم قياسها. Stri_sub سريع جدا، و Seq.int أفضل من SEQ. من السهل أيضا تغيير حجم السلاسل عن طريق تغيير كل 2Ls إلى شيء آخر.

library(stringi)

split_line <- function(x) {
  row_length <- stri_length(x)
  stri_sub(x, seq.int(1L, row_length, 2L), seq.int(2L, row_length, 2L))
}

لم ألاحظ اختلافا عندما كانت قطع السلسلة شخصا طويلان، ولكن لكبرك أكبر، هذا أفضل قليلا.

split_line <- function(x) {
  stri_sub(x, seq.int(1L, stri_length(x), 109L), length = 109L)
}

هنا خيار واحد باستخدام stringi::stri_sub(). وبعد محاولة:

x <- "xxyyxyxy"
stringi::stri_sub(x, seq(1, stringi::stri_length(x), by = 2), length = 2)
# [1] "xx" "yy" "xy" "xy"
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top