Replace matched string with its sub-groups - r

I have some DNA sequences to process, they look like:
>KU508975.1 Acalypha australis maturase K (matK) gene, partial cds; chloroplast
TAAATTATGTGTCAGAGCTATTAATACCTTACCCCATCCATCTAGAAAAATGGGTTCAAATTCTTCGATA
TTGGCTGAAAGATCCCTCTTCTTTGCATTTATTACGACTCTTTCTTCATGAATATTGGAATTGGAACTGT
TTTCTTATTCCAAAGAAATCGATTGCTATTTTTACAAAAAGTAATCCAAGATTTTTCTTGTTTCTATATA
>KC747175.1 Achyranthes bidentata bio-material USDA:GRIN:PI613015 maturase K (matK) gene, partial cds; chloroplast
GATATATTAATACCTTACCCCGCTCATCTAGAAATCTTGGTTCAAACTCTCCGATACTGGTTGAAAGATG
CTTCTTCTTTGCATTTATTACGATTCTTTCTTTATGAGTGTCGTAATTGGATTAGTCTTATTACTCCAAA
AAAATCCATTTCCTTTTTGAAAAAAAGGAATCGAAGATTATTCTTGTTCCTATATAATTTCTATGTATGT
I coded a regex in order to detect the title line of each sequence:
(\>)([A-Z]{2}\d{6}\.?\d)\s([a-zA-Z]+\-?[a-zA-Z]+)\s([a-zA-Z]+\-?[a-zA-Z]+)\s(.*)\n
What function should I use to replace this whole match with its group3 + group4? In addition, I've got 72 matches in one txt file, how can I replace them in one run?

Your current regex won't work for lines where Group 3 or 4 contains a single letter word because [a-zA-Z]+\\-?[a-zA-Z]+ matches 1+ letters, then an optional hyphen, and then again 1+ letters (that means, there must be at least 2 letters). With [a-zA-Z]+(?:-[a-zA-Z]+)?, you can match 1+ letters followed with an optional sequence of - and then 1+ letters.
Also, \s also matches line breaks, and if your title lines are shorter than you assume then .* may grab a sequence line by mistake. You may use \h or [ \t] instead.
Note that \n is not necessary at the end of the pattern because .* matches any 0+ chars other than line break chars with an ICU regex library (it is used in your current code, str_replace_all).
In general, you should only capture with (...) what you need to keep, everything else can be just matched. Remove extra capturing parentheses, and it will save some performance.
If you add (?m)^ at the start, you will make sure you only match > that is at the start of a line.
You may use
"(?m)^>[A-Z]{2}\\d{6}\\.?\\d\\h+([a-zA-Z]+(?:-[a-zA-Z]+)?)\\h+([a-zA-Z]+(?:-[a-zA-Z]+)?).*"
See the regex demo.
Code:
Sequence <- str_replace_all(SequenceRaw,
"(?m)^>[A-Z]{2}\\d{6}\\.?\\d\\h+([a-zA-Z]+(?:-[a-zA-Z]+)?)\\h+([a-zA-Z]+(?:-[a-zA-Z]+)?).*",
"\\1 \\2")

I figured it out myself, with tidyverse packages:
library(tidyverse)
SequenceRaw <- read_file("PATH OF SEQUENCE FILE\\sequenceraw.fasta") ## e.g. sequenceraw.fasta
Sequence <- str_replace_all(SequenceRaw,
"(\\>)([A-Z]{2}\\d{6}\\.?\\d)\\s([a-zA-Z]+\\-?[a-zA-Z]+)\\s([a-zA-Z]+\\-?[a-zA-Z]+)\\s(.*)\\n",
">\\3 \\4\n") ## Keep '>' and add a new line with '\n'
write_file(Sequence, "YOUR PATH\\sequence.fasta")
Here is the result:

Related

regex to find the position of the first four concurrent unique values

I've solved 2022 advent of code day 6, but was wondering if there was a regex way to find the first occurance of 4 non-repeating characters:
From the question:
bvwbjplbgvbhsrlpgdmjqwftvncz
bvwbjplbgvbhsrlpgdmjqwftvncz
# discard as repeating letter b
bvwbjplbgvbhsrlpgdmjqwftvncz
# match the 5th character, which signifies the end of the first four character block with no repeating characters
in R I've tried:
txt <- "bvwbjplbgvbhsrlpgdmjqwftvncz"
str_match("(.*)\1", txt)
But I'm having no luck
You can use
stringr::str_extract(txt, "(.)(?!\\1)(.)(?!\\1|\\2)(.)(?!\\1|\\2|\\3)(.)")
See the regex demo. Here, (.) captures any char into consequently numbered groups and the (?!...) negative lookaheads make sure each subsequent . does not match the already captured char(s).
See the R demo:
library(stringr)
txt <- "bvwbjplbgvbhsrlpgdmjqwftvncz"
str_extract(txt, "(.)(?!\\1)(.)(?!\\1|\\2)(.)(?!\\1|\\2|\\3)(.)")
## => [1] "vwbj"
Note that the stringr::str_match (as stringr::str_extract) takes the input as the first argument and the regex as the second argument.

Regex for alternating pattern between two character groups

I'm trying to find matches where the pattern alternates between two character groups, D\E and R\K\H.
The pattern I've come up with (through reading other posts on here) is
(([DE](?=[RKH])*)|(([RKH])(?=[DE])*))+
Using this pattern with this test string: DREDRDRDRARDK
I get the following matches: DR, DRDRD, RD
I want: DRE, DRDRDR, RDK
The matches are missing the last letter for each group.
Please could someone help me figure out why.
Match the first group followed by the second with all that matched any number of times and then possibly followed by the first group. i.e. ([DE][RKH])+[DE]?, or the same with the groups interchanged, i.e. ([RKH][DE])+[RKH]? or just the first group, i.e. [DE] or just the second group, i.e. [RKH]:
library(gsubfn)
x <- "DREDRDRDRARDK" # input
rx <- "(([DE][RKH])+[DE]?|([RKH][DE])+[RKH]?|[DE]|[RKH])"
strapply(x, rx)
## [[1]]
## [1] "DRE" "DRDRDR" "RDK"
In your pattern, you repeatedly match a single character out of 2 character classes followed by a positive lookahead which asserts that there should be a character present directly at the right.
(Note that the positive lookahead should not be optionally repeated (?=[RKH])* or else it will always be true, matching too much)
If the quantifier * is not present after the lookahead you will get your matches where characters are missing.
The reason why the matches are missing the last letter for each group is when [DE] is matched, there is a positive lookahead asserting what is directly to the right is [RKH] (and the other way around due to the alternation)
It does not match the E in DRE because when matching E the lookahead asserts on of [RKH] after is, which is not the case
It does not match the last R in DRDRDR as there is no A following the last R
As the positive lookahead asserts that there should be a next character present, you also don't match the last K because there is no character after it
As already answered, you can repeatedly match the pairs of character classes followed by optionally matching the first character class after it.
Without the groups, I think it could also be shortened to:
(?:[DE][RKH])+[DE]?|(?:[RKH][DE])+[RKH]?
Regex demo
library(stringr)
str_extract_all("DREDRDRDRARDK", "(?:[DE][RKH])+[DE]?|(?:[RKH][DE])+[RKH]?")
Output
[[1]]
[1] "DRE" "DRDRDR" "RDK"

How to match binomial expressions in R?

I want to match binomials, that is, bisyllabic words, sometimes hyphenated, with slightly varied syllable reduplication; the variation always concerns the first (and, possibly, second) letter in the reduplicated syllable:
x <- c("pow-wow", "pickwick", "easy-peasy", "nitty-gritty", "bzzzzzzz", "mmmmmm", "shish", "wedged", "yaaaaaa")
Here, we have said syllable reduplication in pow-wow, pickwick, easy-peasy, and nitty-gritty (which are then the expected output) but not in bzzzzzzz, mmmmmm, shish, wedged and yaaaaa.
This regex does at least manage to get rid of wedged(which is pronounced as one syllable) as well as monosyllabic words by requiring the presence of a vowel in the capturing group:
grep("\\b\\w?((?!ed)(?=[aeiou])\\w{2,})-?\\w\\w?\\1\\b$", x, value = T, perl = T)
[1] "pow-wow" "pickwick" "easy-peasy" "nitty-gritty" "yaaaaa"
However, yaaaaa is getting matched too. To not match it my feeling is that the capturing group should be disallowed to contain two identical vowels in immediate succession but I don't know how to implement that restriction.
Any ideas?
It looks as though you want to match words that cannot contain ed after the initial chars and 2 or more repeated chars if the same chunk is not found farther in the string. Also, the allowed "difference" window at the start and middle is 0 to 2 characters.
You may use
\b\w{0,2}(?!((.)\2+)(?!.*\1)|ed)([aeiou]\w+)-?\w\w?\3\b
See the regex demo
Details
\b - a word boundary (you may use ^ if your "words" are equal to whole string)
\w{0,2} - two or more word chars (replace with \p{L} to only match letters)
(?!((.)\2+)(?!.*\1)|ed) - no ed or two or more identical chars that do not repeat later in the string are allowed immediately to the right of the current location
([aeiou]\w+) - a vowel (captured in Group 3) and 1+ word chars (replace with \p{L} to only match letters)
-? - an optional hyphen
\w\w? - 1 or 2 word charsd
\3 - same value as captured in Group 3
\b - a word boundary (you may use $ if your "words" are equal to whole string)

Function for regular expression in R

I need to extract certain sequences from a string of text.
Something like 93085k82 will be embedded in text.
Is there a script that identify when 5 numbers, a letter, and then 2 numbers occur?
We can use pattern starting with word boundary (\\b) followed by five digits (\\d{5}), a lower case letter ([a-z]{1}) and two digits (\\d{2}) followed by the word boundary (\\b)
grep("\\b\\d{5}[a-z]{1}\\d{2}\\b", v1)
If we need to extract
library(stringr)
str_extract_all(v1, "\\b\\d{5}[a-z]{1}\\d{2}\\b")

text manipulation in R

I am trying to add parentheses around certain book titles character strings and I want to be able to paste with the paste0 function. I want to take this string:
a <- c("I Like What I Know 1959 02e pdfDrama (amazon.com)", "My Liffe 1993 07e pdfDrama (amazon.com)")
wrap certain strings in parentheses:
a
[1] “I Like What I Know (1959) (02e) (pdfDrama) (amazon.com)”
[2] ”My Life (1993) (07e) (pdfDrama) (amazon.com)”
I have tried but can't figure out a way to replace them within the string:
paste0("(",str_extract(a, "\\d{4}"),")")
paste0("(",str_extract(a, ”[0-9]+.e”),”)”)
Help?
I can suggest a regex for a fixed number of words of specific type:
a <- c("I Like What I Know 1959 02e pdfDrama (amazon.com)","My Life 1993 07e pdfDrama (amazon.com)")
sub("\\b(\\d{4})(\\s+)(\\d+e)(\\s+)([a-zA-Z]+)(\\s+\\([^()]*\\))", "(\\1)\\2(\\3)\\4(\\5)\\6", a)
See the R demo
And here is the regex demo. In short,
\\b(\\d{4}) - captures 4 digits as a whole word into Group 1
(\\s+) - Group 2: one or more whitespaces
(\\d+e) - Group 3: one or more digits and e
(\\s+) - Group 4: ibid
([a-zA-Z]+) - Group 5: one or more letters
(\\s+\\([^()]*\\)) - Group 6: one or more whitespaces, (, zero or more chars other than ( and ), ).
The contents of the groups are inserted back into the result with the help of backreferences.
If there are more words, and you need to wrap words starting with a letter/digit/underscore after a 4-digit word in the string, use
gsub("(?:(?=\\b\\d{4}\\b)|\\G(?!\\A))\\s*\\K\\b(\\S+)", "(\\1)", a, perl=TRUE)
See the R demo and a regex demo
Details:
(?:(?=\\b\\d{4}\\b)|\\G(?!\\A)) - either the location before a 4-digit whole word (see the positive lookahead (?=\\b\\d{4}\\b)) or the end of the previous successful match
\\s* - 0+ whitespaces
\\K - omitting the text matched so far
\\b(\\S+) - Group 1 capturing 1 or more non-whitespace symbols that are preceded with a word boundary.

Resources