Как разбить строку справа налево, как rsplit() в Python?

Предположим, вектор:

xx.1 <- c("zz_ZZ_uu_d", "II_OO_d")

Я хочу получить новый вектор, разделенный справа и только один раз. Ожидаемые результаты:

c("zz_ZZ_uu", "d", "II_OO", "d").

Это было бы похоже на функцию rsplit() Python. Моя текущая идея состоит в том, чтобы перевернуть строку и разделить ее с помощью str_split() в stringr.

Любые лучшие решения?

update
Вот мое решение, возвращающее n разбиений, в зависимости от stringr и stringi. Было бы неплохо, если бы кто-то предоставил версию с базовыми функциями.

rsplit <- function (x, s, n) {
  cc1 <- unlist(stringr::str_split(stringi::stri_reverse(x), s, n))
  cc2 <- rev(purrr::map_chr(cc1, stringi::stri_reverse))
  return(cc2)
}

person ccshao    schedule 08.12.2013    source источник


Ответы (5)


Отрицательный прогноз:

unlist(strsplit(xx.1, "_(?!.*_)", perl = TRUE))
# [1] "zz_ZZ_uu" "d"        "II_OO"    "d"     

Где a(?!b) говорит найти такое a, за которым не следует b. В данном случае .*_ означает, что как бы далеко (.*) не было больше _.

Однако, кажется, не так просто обобщить эту идею. Во-первых, обратите внимание, что его можно переписать как положительный просмотр вперед с помощью _(?=[^_]*$) (найти _, за которым следует что-либо, кроме _, здесь $ означает конец строки). Тогда не очень элегантным обобщением будет

rsplit <- function(x, s, n) {
  p <- paste0("[^", s, "]*")
  rx <- paste0(s, "(?=", paste(rep(paste0(p, s), n - 1), collapse = ""), p, "$)")
  unlist(strsplit(x, rx, perl = TRUE))
}

rsplit(vec, "_", 1)
# [1] "a_b_c_d_e_f" "g"           "a"           "b"          
rsplit(vec, "_", 3)
# [1] "a_b_c_d" "e_f_g"   "a_b"    

где, например. в случае n=3 эта функция использует _(?=[^_]*_[^_]*_[^_]*$).

person Julius Vainora    schedule 08.12.2013
comment
Я не знаком с Perl, не могли бы вы немного объяснить, и как мне изменить его, если я хочу разделить два или более _? - person ccshao; 08.12.2013
comment
Большое спасибо @Julius! - person ccshao; 09.12.2013

Еще два. В обоих я использую "(.*)_(.*)" в качестве шаблона для захвата обеих частей строки. Помните, что * является жадным, поэтому первый (.*) будет соответствовать как можно большему числу символов.

Здесь я использую regexec, чтобы зафиксировать, где начинаются и заканчиваются ваши подсказки, и regmatches, чтобы восстановить их:

unlist(lapply(regmatches(xx.1, regexec("(.*)_(.*)", xx.1)),
              tail, -1))

И это немного менее академично, но легко понять:

unlist(strsplit(sub("(.*)_(.*)", "\\1@@@\\2", xx.1), "@@@"))
person flodel    schedule 08.12.2013

Как насчет того, чтобы просто склеить его вместе после того, как он раскололся?

rsplit <- function( x, s ) {
  spl <- strsplit( "zz_ZZ_uu_d", s, fixed=TRUE )[[1]]
  res <- paste( spl[-length(spl)], collapse=s, sep="" )
  c( res, spl[length(spl)]  )
}
> rsplit("zz_ZZ_uu_d", "_")
[1] "zz_ZZ_uu" "d"  
person Ari B. Friedman    schedule 08.12.2013

Я также думал об очень похожем подходе к подходу Ари.

> res <- lapply(strsplit(xx.1, "_"), function(x){
    c(paste0(x[-length(x)], collapse="_" ), x[length(x)])
  }) 

> unlist(res)
[1] "zz_ZZ_uu" "d"        "II_OO"    "d"  
person Jilber Urbina    schedule 08.12.2013

Это дает именно то, что вы хотите, и это самый простой подход:

require(stringr)
as.vector(t(str_match(xx.1, '(.*)_(.*)') [,-1]))
[1] "zz_ZZ_uu" "d"        "II_OO"    "d"

Объяснение:

  • str_split() - это не тот дроид, которого вы ищете, потому что он выполняет разделение только слева направо, а разделение и повторная вставка всех (n-1) крайних левых совпадений - пустая трата времени. Поэтому используйте str_split() с регулярным выражением с двумя группами захвата. Обратите внимание, что первый (.*)_ будет жадно сопоставлять все до последнего вхождения _, что вам и нужно. (Это не удастся, если не будет хотя бы одного _, и вернет NA)
  • str_match() возвращает матрицу, в которой первый столбец представляет собой всю строку, а последующие столбцы — отдельные группы захвата. Нам не нужен первый столбец, поэтому удалите его с помощью [,-1]
  • as.vector() развернет эту матрицу по столбцам, а это не то, что вам нужно, поэтому мы используем t(), чтобы транспонировать ее для развертывания по строкам.
  • str_match(string, pattern) векторизован как по строке, так и по шаблону, что очень удобно
person smci    schedule 21.09.2016
comment
Кстати, если вы делаете много этого, определите пользовательскую функцию str_rsplit(...) <- function(...) { ... } - person smci; 21.09.2016