R: найти, находится ли число в пределах диапазона в строке символов

У меня есть строка s, где "подстроки" разделены вертикальной чертой. Подстроки могут содержать или не содержать числа. И у меня есть тестовая строка символов n, которая содержит число и может содержать или не содержать буквы. См. пример ниже. Обратите внимание, что интервал может быть любым.

Я пытаюсь удалить все подстроки, где n не находится в диапазоне или не является точным совпадением. Я понимаю, что мне нужно разделить на -, преобразовать в числа и сравнить низкий/высокий уровень с n, преобразованным в числовой. Вот моя отправная точка, но затем я застрял с получением окончательной хорошей строки из unl_new.

s = "liquid & bar soap 1.0 - 2.0oz | bar 2- 5.0 oz | liquid soap 1-2oz | dish 1.5oz"
n = "1.5oz"

unl = unlist(strsplit(s,"\\|"))

unl_new = (strsplit(unl,"-"))
unl_new = unlist(gsub("[a-zA-Z]","",unl_new))

Желаемый результат:

"liquid & bar soap 1.0 - 2.0oz | liquid soap 1-2oz | dish 1.5oz"

Я полностью на неправильном пути? Спасибо!


person Alexey Ferapontov    schedule 08.09.2015    source источник


Ответы (3)


Здесь вариант с использованием r-base ;

## extract the n numeric
nn <- as.numeric(gsub("[^0-9|. ]", "", n))
## keep only numeric and -( for interval)
## and split by |
## for each interval test the condition to create a boolean vector
contains_n <- sapply(strsplit(gsub("[^0-9|. |-]", "", s),'[|]')[[1]],
       function(x){
         yy <- strsplit(x, "-")[[1]]
         yy <- as.numeric(yy[nzchar(yy)])
         ## the condition
         (length(yy)==1 && yy==nn) || length(yy)==2 && nn >= yy[1] && nn <= yy[2]
       })

## split again and use the boolean factor to remove the parts 
## that don't respect the condition
## paste the result using collapse to get a single character again
paste(strsplit(s,'[|]')[[1]][contains_n],collapse='')

## [1] "liquid & bar soap 1.0 - 2.0oz  liquid soap 1-2oz  dish 1.5oz"
person agstudy    schedule 08.09.2015

Не знаю, достаточно ли это общее, но вы можете попробовать:

require(stringr)
splitted<-strsplit(s,"\\|")[[1]]
ranges<-lapply(strsplit(
          str_extract(splitted,"[0-9\\.]+(\\s*-\\s*[0-9\\.]+|)"),"\\s*-\\s*"),
          as.numeric)
tomatch<-as.numeric(str_extract(n,"[0-9\\.]+"))
paste(splitted[
            vapply(ranges, function(x) (length(x)==1 && x==tomatch) || (length(x)==2 && findInterval(tomatch,x)==1),TRUE)],
             collapse="|")
#[1] "liquid & bar soap 1.0 - 2.0oz | liquid soap 1-2oz | dish 1.5oz"
person nicola    schedule 08.09.2015
comment
если n=2.3oz, вывод по-прежнему включает dish 1.5oz - person Alexey Ferapontov; 08.09.2015
comment
Спасибо! Теперь работает как положено - person Alexey Ferapontov; 08.09.2015

Вот метод, начинающийся с вашего шага unl с использованием stringr:

unl = unlist(strsplit(s,"\\|"))
n2 <- as.numeric(gsub("[[:alpha:]]*", "", n))
num_lst <- str_extract_all(unl, "\\d\\.?\\d*")
indx <- lapply(num_lst, function(x) {
  if(length(x) == 1) {isTRUE(all.equal(n2, as.numeric(x))) 
  } else {n2 >= as.numeric(x[1]) & n2 <= as.numeric(x[2])}})

paste(unl[unlist(indx)], collapse=" | ")
[1] "liquid & bar soap 1.0 - 2.0oz  |  liquid soap 1-2oz  |  dish 1.5oz"

Я также проверил его с другими суммами, такими как "2.3oz". С помощью n2 мы приводим n к числовому для сравнения. Переменная num_lst изолирует числа от строки символов.

С помощью indx мы применяем наши сравнения к номерам строк. если есть одно число, мы проверяем, равно ли оно n2. Я решил не использовать базовый оператор ==, чтобы избежать проблем с округлением. Вместо этого используется isTRUE(all.equal(x, y)).

Наконец, переменная логического индекса indx используется для подмножества строки символов, чтобы извлечь совпадения и вставить их вместе с конвейером "|".

person Pierre L    schedule 08.09.2015
comment
В каком пакете существует lengths()? Не могу найти в help - person Alexey Ferapontov; 08.09.2015
comment
Это базовая функция в R 3.2.0. - person Pierre L; 08.09.2015
comment
Его можно заменить на unlist(lapply(num_lst, length)) == 1 - person Pierre L; 08.09.2015
comment
Работает на 1.5oz, производит NA|NA|NA|NA на 2.3oz - person Alexey Ferapontov; 08.09.2015
comment
Обновлено с помощью isTRUE - person Pierre L; 08.09.2015
comment
Спасибо! Работает сейчас. Не могу принять более 1 ответа. Проголосовал за - person Alexey Ferapontov; 08.09.2015