заменить диапазон чисел отдельными числами в строке символов

Есть ли способ заменить диапазон чисел отдельными числами в строке символов? Число может варьироваться от n до n, скорее всего, от 1 до 15, также возможно от 4 до 10.

диапазон может быть указан с помощью a) -

a <- "I would like to buy 1-3 cats"

или со словом б) например: to, bis, jusqu'à

b <- "I would like to buy 1 jusqu'à 3 cats"

Результаты должны выглядеть так

"I would like to buy 1,2,3 cats"

Я нашел это: Заменить диапазон чисел определенным числом, но не смог действительно используйте его в R.


person captcoma    schedule 18.03.2018    source источник
comment
Какие регулярные выражения вы пробовали? Если ничего, я бы посмотрел на gregexpr и regmatches.   -  person r2evans    schedule 18.03.2018
comment
@ r2evans Это сложно, потому что замена использует функции групп захвата, а не сами группы захвата.   -  person Tim Biegeleisen    schedule 18.03.2018
comment
@Sathish А как насчет to и bis?   -  person Tim Biegeleisen    schedule 18.03.2018
comment
Есть много хитрых мелочей, которые могут повлиять на то, будет ли работать данное решение регулярного выражения. Написание хорошего регулярного выражения включает в себя сначала описание всех типов входных данных, которые вы хотите сопоставить, и, что не менее важно, всех видов входных данных, которые вы НЕ хотите сопоставлять.   -  person De Novo    schedule 18.03.2018


Ответы (3)


gsubfn в пакете gsubfn похож на gsub, но вместо замены совпадения заменяющей строкой он позволяет пользователю указать функцию (возможно, в виде формулы, как здесь). Затем он передает совпадения группам захвата в регулярном выражении, т. е. совпадения с частями регулярного выражения в скобках, в качестве отдельных аргументов и заменяет все совпадение выходными данными функции. Таким образом, мы сопоставляем "(\\d+)(-| to | bis | jusqu'à )(\\d+)", что приводит к трем группам захвата, поэтому 3 аргумента функции. В функции мы используем seq с первым и третьим из них. Обратите внимание, что seq может принимать символьные аргументы и интерпретировать их как числовые, поэтому нам не нужно преобразовывать аргументы в числовые.

Таким образом, мы получаем этот однострочный:

library(gsubfn)
s <- c(a, b) # test input strings

gsubfn("(\\d+)(-| to | bis | jusqu'à )(\\d+)", ~ paste(seq(..1, ..3), collapse = ","), s)

давая:

[1] "I would like to buy 1,2,3 cats" "I would like to buy 1,2,3 cats"
person G. Grothendieck    schedule 18.03.2018
comment
Обратите внимание, что когда gsubfn применяется к вектору, содержащему NA значений, они преобразуются в символы NA. - person Brian D; 22.05.2020

Не самый эффективный, но...

s <- c("I would like to buy 1-3 cats",
       "I would like to buy 1 jusqu'à 3 cats",
       "foo 22-33",
       "quux 11-3 bar")

gre <- gregexpr("([0-9]+(-| to | bis | jusqu'à )[0-9]+)", s)
gre2 <- gregexpr('[0-9]+', regmatches(s, gre))

regmatches(s, gre) <- lapply(regmatches(regmatches(s, gre), gre2),
                             function(a) paste(do.call(seq, as.list(as.integer(a))), collapse = ","))
s
# [1] "I would like to buy 1,2,3 cats"          "I would like to buy 1,2,3 cats"         
# [3] "foo 22,23,24,25,26,27,28,29,30,31,32,33" "quux 11,10,9,8,7,6,5,4,3 bar"           
person r2evans    schedule 18.03.2018
comment
не работает для общего случая множественных совпадений, где пригодятся группы захвата: например, s <- c("I would like to buy 1-3 cats or 4-6 dogs") - person Brian D; 22.05.2020

На самом деле это немного сложно, если только кто-то уже не написал пакет, который делает это (о чем я не знаю).

a <- "I would like to buy 1-3 cats"
pos <- unlist(gregexpr("\\d+\\D+", a))
a_split <- unlist(strsplit(a, ""))
replacement <- paste(seq.int(a_split[pos[1]], a_split[pos[2]]), collapse = ",")
gsub("\\d+\\D+\\d+", replacement, a)
# [1] "I would like to buy 1,2,3 cats"

РЕДАКТИРОВАТЬ: Чтобы показать, что одно и то же решение работает для произвольных нецифровых символов между двумя числами:

b <- "I would like to buy 1 jusqu'à 3 cats"
pos_b <- unlist(gregexpr("\\d+\\D+", b))
b_split <- unlist(strsplit(b, ""))
replacement <- paste(seq.int(b_split[pos_b[1]], b_split[pos_b[2]]), collapse = ",")
gsub("\\d+\\D+\\d+", replacement, b)
# [1] "I would like to buy 1,2,3 cats"

Вы можете добавить произвольные требования для запуска нецифровых символов, если хотите. Если вам нужна помощь с этим, просто поделитесь ограничениями на слова или символы, которые находятся между числами!

person De Novo    schedule 18.03.2018