Подмножество строк путем подсчета определенных символов - PullRequest
0 голосов
/ 27 декабря 2018

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

strings <- c("ABBSDGNHNGA", "AABSDGDRY", "AGNAFG", "GGGDSRTYHG") 

Я хочу обрезать строку, как только число вхождений A, G и N достигнет определенного значения, скажем 3. В этом случае, результат должен быть:

some_function(strings)

c("ABBSDGN", "AABSDG", "AGN", "GGG") 

Я пытался использовать выражения stringi, stringr и regex, но я не могу понять это.

Ответы [ 5 ]

0 голосов
/ 28 декабря 2018

Вы можете выполнить свою задачу с помощью простого вызова str_extract из пакета stringr :

library(stringr)

strings <- c("ABBSDGNHNGA", "AABSDGDRY", "AGNAFG", "GGGDSRTYHG")

str_extract(strings, '([^AGN]*[AGN]){3}')
# [1] "ABBSDGN" "AABSDG"  "AGN"     "GGG"

Часть [^AGN]*[AGN] шаблона регулярного выражения говорит, что нужно искать нольили более последовательных символов, которые не являются A, G или N, за которыми следует один экземпляр A, G или N. Дополнительная обертка с круглыми скобками и фигурными скобками, например ([^AGN]*[AGN]){3}, означает, что нужно искать этот шаблон три раза подряд.Вы можете изменить количество вхождений A, G, N, которое вы ищете, изменив целое число в фигурных скобках:

str_extract(strings, '([^AGN]*[AGN]){4}')
# [1] "ABBSDGNHN"  NA           "AGNA"       "GGGDSRTYHG"

Есть несколько способов выполнить вашу задачу с использованием базовых функций R,Одним из них является использование regexpr, за которым следует regmatches:

m <- regexpr('([^AGN]*[AGN]){3}', strings)
regmatches(strings, m)
# [1] "ABBSDGN" "AABSDG"  "AGN"     "GGG"

В качестве альтернативы вы можете использовать sub:

sub('(([^AGN]*[AGN]){3}).*', '\\1', strings)
# [1] "ABBSDGN" "AABSDG"  "AGN"     "GGG"
0 голосов
/ 28 декабря 2018

Это просто версия без strsplit для Маврикия Эверса аккуратное решение .

sapply(strings,
       function(x) {
         raw <- rawToChar(charToRaw(x), multiple = TRUE)
         idx <- which.max(cumsum(raw %in% c("A", "G", "N")) == 3)
         paste(raw[1:idx], collapse = "")
       })
## ABBSDGNHNGA   AABSDGDRY      AGNAFG  GGGDSRTYHG 
##   "ABBSDGN"    "AABSDG"       "AGN"       "GGG"

Или, немного отличается, без strsplit и paste:

test <- charToRaw("AGN")
sapply(strings,
       function(x) {
         raw <- charToRaw(x)
         idx <- which.max(cumsum(raw %in% test) == 3)
         rawToChar(raw[1:idx])
       })
0 голосов
/ 27 декабря 2018

Определите позиции паттерна, используя gregexpr, затем извлеките n-ю позицию (3) и поместите все от 1 в эту n-ю позицию, используя subset.

nChars <- 3
pattern <- "A|G|N"
# Using sapply to iterate over strings vector
sapply(strings, function(x) substr(x, 1, gregexpr(pattern, x)[[1]][nChars]))

PS:

Если есть строка, у которой нет 3 совпадений, она сгенерирует NA, поэтому вам просто нужно использовать na.omit для окончательного результата.

0 голосов
/ 27 декабря 2018

Интересная проблема.Я создал функцию (см. Ниже), которая решает вашу проблему.Предполагается, что в ваших строках есть только буквы и никаких специальных символов.

 reduce_strings = function(str, chars, cnt){

  # Replacing chars in str with "!"
  chars = paste0(chars, collapse = "")
  replacement = paste0(rep("!", nchar(chars)), collapse = "")
  str_alias = chartr(chars, replacement, str) 

  # Obtain indices with ! for each string
  idx = stringr::str_locate_all(pattern = '!', str_alias)

  # Reduce each string in str
  reduce = function(i) substr(str[i], start = 1, stop = idx[[i]][cnt, 1])
  result = vapply(seq_along(str), reduce, "character")
  return(result)
}

# Example call
str = c("ABBSDGNHNGA", "AABSDGDRY", "AGNAFG", "GGGDSRTYHG") 
chars = c("A", "G", "N") # Characters that are counted
cnt = 3 # Count of the characters, at which the strings are cut off
reduce_strings(str, chars, cnt) # "ABBSDGN" "AABSDG" "AGN" "GGG"
0 голосов
/ 27 декабря 2018

Вот базовая опция R с использованием strsplit

sapply(strsplit(strings, ""), function(x)
    paste(x[1:which.max(cumsum(x %in% c("A", "G", "N")) == 3)], collapse = ""))
#[1] "ABBSDGN" "AABSDG"  "AGN"     "GGG"

Или в tidyverse

library(tidyverse)
map_chr(str_split(strings, ""), 
    ~str_c(.x[1:which.max(cumsum(.x %in% c("A", "G", "N")) == 3)], collapse = ""))
...