Подмножество строк путем подсчета определенных символов
У меня есть следующие строки:
strings <- c("ABBSDGNHNGA", "AABSDGDRY", "AGNAFG", "GGGDSRTYHG")
Я хочу отрезать строку, как только число вхождений A, G и N достигнет определенного значения, скажем 3. В этом случае результат должен быть:
some_function(strings)
c("ABBSDGN", "AABSDG", "AGN", "GGG")
Я пытался использовать stringi
, stringr
и регулярные выражения выражения, но я не могу понять это.
Ответы
Ответ 1
Вы можете выполнить свою задачу с помощью простого вызова 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"
Ответ 2
Вот базовый вариант 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 = ""))
Ответ 3
Определите позиции шаблона, используя gregexpr
затем извлеките n-ю позицию (3
) и выведите все subset
из 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
для окончательного результата.
Ответ 4
Это просто версия без strsplit
к Maurits Эверс аккуратным раствора.
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])
})
Ответ 5
Интересная проблема. Я создал функцию (см. Ниже), которая решает вашу проблему. Предполагалось, что в любой из ваших строк есть только буквы и никаких специальных символов.
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"