J'ai les chaînes suivantes:
strings <- c("ABBSDGNHNGA", "AABSDGDRY", "AGNAFG", "GGGDSRTYHG")
Je veux couper la chaîne dès que le nombre d'occurrences de A, G et N atteint une certaine valeur, disons 3. Dans ce cas, le résultat devrait être:
some_function(strings)
c("ABBSDGN", "AABSDG", "AGN", "GGG")
J'ai essayé d'utiliser les expressions stringi
, stringr
et regex mais je n'arrive pas à le comprendre.
Vous pouvez accomplir votre tâche avec un simple appel à str_extract
depuis le stringr package:
library(stringr)
strings <- c("ABBSDGNHNGA", "AABSDGDRY", "AGNAFG", "GGGDSRTYHG")
str_extract(strings, '([^AGN]*[AGN]){3}')
# [1] "ABBSDGN" "AABSDG" "AGN" "GGG"
La partie [^AGN]*[AGN]
du motif regex indique de rechercher zéro ou plusieurs caractères consécutifs autres que A, G ou N, suivis d'une instance de A, G ou N. L'enroulement supplémentaire avec des parenthèses et des accolades, comme ceci ([^AGN]*[AGN]){3}
, signifie rechercher ce modèle trois fois de suite. Vous pouvez modifier le nombre d'occurrences de A, G, N que vous recherchez en modifiant le nombre entier entre accolades:
str_extract(strings, '([^AGN]*[AGN]){4}')
# [1] "ABBSDGNHN" NA "AGNA" "GGGDSRTYHG"
Il existe plusieurs façons d’accomplir votre tâche à l’aide des fonctions de base R. L’une consiste à utiliser regexpr
suivi de regmatches
:
m <- regexpr('([^AGN]*[AGN]){3}', strings)
regmatches(strings, m)
# [1] "ABBSDGN" "AABSDG" "AGN" "GGG"
Vous pouvez également utiliser sub
:
sub('(([^AGN]*[AGN]){3}).*', '\\1', strings)
# [1] "ABBSDGN" "AABSDG" "AGN" "GGG"
Voici une option de base R utilisant 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"
Ou dans la tidyverse
library(tidyverse)
map_chr(str_split(strings, ""),
~str_c(.x[1:which.max(cumsum(.x %in% c("A", "G", "N")) == 3)], collapse = ""))
Identifiez les positions du motif à l'aide de gregexpr
, puis extrayez la n-ième position (3
) et sous-chaînes, de 1
à cette n-ième position à l'aide de 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:
S'il y a une chaîne qui n'a pas 3 correspondances, elle générera NA
, il vous suffit donc d'utiliser na.omit
sur le résultat final.
Ceci est juste une version sans strsplit
de Maurits Evers solution soignée .
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"
Ou, légèrement différent, sans strsplit
et paste
:
test <- charToRaw("AGN")
sapply(strings,
function(x) {
raw <- charToRaw(x)
idx <- which.max(cumsum(raw %in% test) == 3)
rawToChar(raw[1:idx])
})
Problème intéressant. J'ai créé une fonction (voir ci-dessous) qui résout votre problème. Il est supposé qu'il n'y a que des lettres et aucun caractère spécial dans aucune de vos chaînes.
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"