Эффективно разбить строку на основе n-го вхождения подстроки, используя R - PullRequest
1 голос
/ 04 апреля 2019

Введение

Учитывая строку в R, возможно ли получить векторизованное решение (т.е. без циклов), где мы можем разбить строку на блоки, где каждый блок определяетсяn-е вхождение подстроки в строку.

Работа, выполненная с воспроизводимым примером

Предположим, у нас есть несколько абзацев известного текста Lorem Ipsum.

library(strex)
# devtools::install_github("aakosm/lipsum")
library(lipsum)

my.string = capture.output(lipsum(5))
my.string = paste(my.string, collapse = " ")

> my.string # (partial output)
# [1] "Lorem ipsum dolor ... id est laborum. "

Мы хотели бы разбить этот текст на сегменты в каждом 3-м вхождении слова "в" (пробелвключен для того, чтобы отличать слова, которые содержат «in» как часть их (например, «min»).

У меня есть следующее решение с циклом while:

# We wish to break up the string at every 
# 3rd occurence of the worn "in"

break.character = " in"
break.occurrence = 3
string.list = list()
i = 1

# initialize string to send into the loop
current.string = my.string

while(length(current.string) > 0){

  # Enter segment into the list which occurs BEFORE nth occurence character of interest
  string.list[[i]] = str_before_nth(current.string, break.character, break.occurrence)

  # Update next string to exmine.
  # Next string to examine is current string AFTER nth occurence of character of interest
  current.string = str_after_nth(current.string, break.character, break.occurrence)

  i = i + 1
}

Мы можем получить желаемый результат в списке с предупреждением (предупреждение не показано)

> string.list (#partial output shown)
[[1]]
[1] "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit"

[[2]]
[1] " voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor"
...

[[6]]
[1] " voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor"

Цель

Можно ли улучшить это решение путем векторизации(то есть используя apply(), lapply(), mapply() и т. д.).Кроме того, мое текущее решение обрезает последнее вхождение подстроки в блоке.

Текущее решение может не работать хорошо на очень длинных строках (таких как последовательности ДНК, где мы ищем блоки с n-м вхождениемподстрока нуклеотидов).

Ответы [ 2 ]

1 голос
/ 04 апреля 2019

Дайте мне знать, если это поможет.Я постараюсь сделать это быстрее.Он удерживает третий in в блоке кода.Если это сработает, я тоже буду комментировать.

library(lipsum)
library(stringi)

my.string = capture.output(lipsum(5))
my.string = paste(my.string, collapse = " ")

end_of_in <- stri_locate_all(fixed = " in ", my.string)[[1]][,2]
start_of_strings <- c(1, end_of_in[c(F, F, T)]) 
end_of_strings <- c(end_of_in[c(F, F, T)] - 1, nchar(my.string))
end_of_strings <- end_of_strings[!duplicated(end_of_strings)]


stri_sub(my.string, start_of_strings, end_of_strings)

РЕДАКТИРОВАТЬ: на самом деле используйте stri_sub из stringi.Это будет масштабироваться намного лучше, чем substring.Смотри:

my.string <- paste(rep(my.string, 10000), collapse = " ")
nchar(my.string)
[1] 22349999

microbenchmark::microbenchmark(
  sol1 = {
    text_split=strsplit(my.string," in ")[[1]]

    l=length(text_split)
    n = floor(l/3)
    Seq = seq(1,by=2,length.out = n)

    L= list()
    L=sapply(Seq, function(x){
      paste0(paste(text_split[x:(x+2)],collapse=" in ")," in ")
    })
    if (l>(n*3)){
      L = c(L,paste(text_split[(n*3+1):l],collapse=" in "))
    }
  },
  sol2 = {
    end_of_in <- stri_locate_all(fixed = " in ", my.string)[[1]][,2]
    start_of_strings <- c(1, end_of_in[c(F, F, T)]) 
    end_of_strings <- c(end_of_in[c(F, F, T)] - 1, nchar(my.string))
    end_of_strings <- end_of_strings[!duplicated(end_of_strings)]
    stri_sub(my.string, start_of_strings, end_of_strings)
  },
  times = 10
)

Unit: milliseconds
 expr      min        lq      mean    median        uq       max neval
 sol1 914.1268 927.45958 941.36117 939.80361 950.18099 980.86941    10
 sol2  55.4163  56.40759  58.53444  56.86043  57.03707  71.02974    10
1 голос
/ 04 апреля 2019

Попробуйте с этим:

text_split=strsplit(text," in ")[[1]]

l=length(text_split)
n = floor(l/3)
Seq = seq(1,by=2,length.out = n)

L= list()
L=sapply(Seq, function(x){
  paste0(paste(text_split[x:(x+2)],collapse=" in ")," in ")
})
if (l>(n*3)){
L = c(L,paste(text_split[(n*3+1):l],collapse=" in "))
}

Последнее условие в случае, если число in не делится на 3. Кроме того, есть последнее in, вставленное в sapply(), потому что я не знаю, что вы хотите сделать с одним in отделяет ваши блоки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...