This is on request to ask a new question based on a previous one I asked here: I have a list of compounds like the following:
Ag0.05Zr1.0
Al0.11W1.0
Al0.18Cr1.0
AlFe
AlFe0.2NiCuCoCr
AlFe0.2NiCuCoCz
AlFeNi
AlFeNiCo
AlFeNiCrCo
AlFeNiCrCoCu0.2
AlFeNiCu0.2CoCr
Cr1.0Mo0.33
U0.33Zr1.0
V0.33W1.0
V1.0W1.0
I need to split and place an underscore in between element names. Now the element names can be single Capital letter, or 1 Capital letter followed by a small letter. I have achieved so far placing a '_' in between strings containing 2 letters. However whenever a Single letter element is concerned it is giving the following:
"Element V1.0 in compound Co_1.0_Cu_1.0_Fe_1.0_5_Nb_0.5_6_Ni_1.0 entered does not exist!". (Error message generated by the code) However I want it to be the following:
"V_1.0_Co_1.0_Cu_1.0_Fe_1.05_Nb_0.56_Ni_1.0"
So it is not only grouping single letters with numbers, it is also failing to identify all numbers grouped together. Can anyone please help? I used the following code to achieve this:
elem = gsub("(?<=[a-z0-9])(?=[0-9A-Z])", "_", elem, perl = TRUE)
where elem is my list of compounds.
The numbers are basically element fractions, So the error message should read like:
Error: Element 'Cz' (or 'Z' a single letter element which does not exist in the periodic table), " in compound xyz entered does not exist!
You can provide multiple replacement patterns in gsub using |.
# data
elem <- c("Ag0.05Zr1.0", "Al0.11W1.0", "Al0.18Cr1.0", "AlFe", "AlFe0.2NiCuCoCr",
"AlFe0.2NiCuCoCz", "AlFeNi", "AlFeNiCo", "AlFeNiCrCo", "AlFeNiCrCoCu0.2",
"AlFeNiCu0.2CoCr", "Cr1.0Mo0.33", "U0.33Zr1.0", "V0.33W1.0", "V1.0W1.0")
# solution
elem <- gsub("(?<=[a-z0-9])(?=[A-Z])|(?<=[a-z])(?=[0-9])|(?<=[A-Z])(?=[0-9])",
"_", elem, perl = TRUE)
elem
"Ag_0.05_Zr_1.0"
"Al_0.11_W_1.0"
"Al_0.18_Cr_1.0"
"Al_Fe"
"Al_Fe_0.2_Ni_Cu_Co_Cr"
"Al_Fe_0.2_Ni_Cu_Co_Cz"
"Al_Fe_Ni"
"Al_Fe_Ni_Co"
"Al_Fe_Ni_Cr_Co"
"Al_Fe_Ni_Cr_Co_Cu_0.2"
"Al_Fe_Ni_Cu_0.2_Co_Cr"
"Cr_1.0_Mo_0.33"
"U_0.33_Zr_1.0"
"V_0.33_W_1.0"
"V_1.0_W_1.0"
I made two changes to the gsub in the original post:
I changed the replacement pattern (?<=[a-z0-9])(?=[0-9A-Z]) to two separate replacement patterns (?<=[a-z0-9])(?=[A-Z])|(?<=[a-z])(?=[0-9]) because the original replacement pattern inserted "_" between double digits as well (e.g., "Cr_1.0_Mo_0.3_3").
I added a third replacement pattern (?<=[A-Z])(?=[0-9]) to insert "_" between capital letters and numbers.
Related
I have a vector with some codes. However, for an unknown reason, some of the code start with X# (# being a number 0-9). If my vector item does start with x#, I need to remove the first two letters.
Examples:
codes <- c('x0fa319-432f39-4fre78', '23weq0-4fsf198-417203', 'x2431-5435-1242-qewf')
expectedResult <- c('fa319-432f39-4fre78', '23weq0-4fsf198-417203', '431-5435-1242-qewf')
I tried using str_replace and gsub, but I couldn't get it right:
gsub("X\\d", "", codes)
but this would remove the x# even if it was in the middle of the string.
Any ides?
You can use
codes <- c('x0fa319-432f39-4fre78', '23weq0-4fsf198-417203', 'x2431-5435-1242-qewf')
sub("^x\\d", "", codes, ignore.case=TRUE)
See the R demo.
The ^x\d pattern matches x and any digit at the start of a string.
sub replaces the first occurrence only.
ignore.case=TRUE enables case insensitive matching.
I have a dataframe (data) with a column containing text from reports (data$Report_Text). I need to extract 40 characters before and after a keyword (including the keyword) for each row and store as a new column in the dataframe.
So far I have this for the characters before (ideally would like to store the text before + after in one column, but if that isn't possible I can do two columns):
data$characters <- sub('.*?(\\d{40}) keyword', "", data$Report_Text)
However when I run this, it gives me all of the text before the keyword, not just 40 characters. Where am I going wrong?
data$characters <- gsub("^.*(.{40}keyword.{40}).*$", "\\1", data$Report_Text))
posibly changing the . before the {40} by \\d (only digits) or the character type of your preference.
I wanted to extract certain words from a bigger word-list. One example of a desired extracted word-list is: extract all the words that contain /s/ followed by /r/. So this should give me words such as sər'ka:rəh, e:k'sa:r, səmʋitərəɳ, and so:'ha:rd. from the bigger word-list.
Consider the data (IPA transcription) to be the one given below:
sər'ka:rəh
sə'lᴔ:nija:
hã:ki:
pu:'dʒa:ẽ:
e:k'sa:r
mritko:
dʒʱã:sa:
pə'hũtʃ'ne:'ʋa:le:
kərəpʈ
tʃinhirit
tʃʰəʈʈʰi:
dʱũdʱ'la:pən
səmʋitərəɳ
so:'ha:rd
məl'ʈi:spe:'ʃijliʈi:
la:'pər'ʋa:i:
upləbɡʱ
Thanks much!
Here's an answer to the issue described in the first paragraph of your post. (To my mind, the examples in the second paragraph are inconsistent with the issue described in the first para, so I'll take the liberty of ignoring them here).
You say you want to "extract all the words that contain p followed by t". The word 'extract' implies that there are other characters in the same string than those you want to match and extract. The verb 'contain' implies that the words you want to extract need not necessarily have p in word-initial position. Based on these premises, here's some mock data and a solution to the task:
Data:
x <- c("pastry is to the pastor's appetite what pot is to the pupil's")
Solution:
libary(stringr)
unlist(str_extract_all(x, "\\b\\w*(?<=p)\\w*t\\w*\\b"))
This uses word boundaries \\b to extract the target words from the surrounding context; it further uses positive lookbehind (?<=...) to assert the condition that for there to be a matching t there needs to be a p character occurring prior to the match.
The regex in more detail:
\\b: the opening word boundary
\\w*: zero or more alphanumeric chars (or an underscore)
(?<=p): positive lookbehind: "if and only if you see a p char on
the left..."
\\w*: zero or more alphanumeric chars (or an underscore)
t: the literal character t
\\w*: zero or more alphanumeric chars (or an underscore)
\\b: the closing word boundary
Result:
[1] "pastry" "pastor" "appetite" "pot"
EDIT 1:
Now that the question has been updated, a more definitive answer is possible.
Data:
x <- c("sər'ka:rəh","sə'lᴔ:nija:","hã:ki:","pu:'dʒa:ẽ:","e:k'sa:r",
"mritko:","dʒʱã:sa:","pə'hũtʃ'ne:'ʋa:le:","kərəpʈ","tʃinhirit",
"tʃʰəʈʈʰi:","dʱũdʱ'la:pən","səmʋitərəɳ","so:'ha:rd",
"məl'ʈi:spe:'ʃijliʈi:", "la:'pər'ʋa:i:","upləbɡʱ")
If you want to match (rather than extract) words that "contain /s/ followed by /r/", you can use grepin various ways. Here are two ways:
grep("s.*r", x, value = T)
or:
grep("(?<=s).*r", x, value = T, perl = T) # with lookbehind
The result is the same in either case:
[1] "sər'ka:rəh" "e:k'sa:r" "səmʋitərəɳ" "so:'ha:rd"
EDIT 2:
If the aim is to match words that "contain /s/ or /p/ followed by /r/ or /t/", you can use the metacharacter | to allow for alternatives:
grep("s.*r|s.*t|p.*r|p.*t", x, value = T)
# or, more succinctly:
grep("(s|p).*(r|t)", x, value = T)
[1] "sər'ka:rəh" "e:k'sa:r" "pə'hũtʃ'ne:'ʋa:le:" "səmʋitərəɳ" "so:'ha:rd"
[6] "la:'pər'ʋa:i:"
You can use grep function. Assuming your list is called list:
grep("p[a-z]+t", list, value=TRUE)
I had a data.frame with some categorical variables. Let's suppose sentences is one of these variables:
sentences <- c("Direito à participação e ao controle social",
"Direito a ser ouvido pelo governo e representantes",
"Direito aos serviços públicos",
"Direito de acesso à informação")
For each value, I would like to extract just the first letter of each word, ignoring if the word has 4 letters or less (e, de, à, a, aos, ser, pelo), My goal is create acronym variables. I expect the following result:
[1] "DPCS", "DOGR", "DSP", "DAI
I tried to make a pattern subset using stringr with a regex pattern founded here:
library(stringr)
pattern <- "^(\b[A-Z]\w*\s*)+$"
str_subset(str_to_upper(sentences), pattern)
But I got an error when creating the pattern object:
Error: '\w' is an escape sequence not recognized in the string beginning with ""^(\b[A-Z]\w"
What am I doing wrong?
Thanks in advance for any help.
You can use gsub to delete all the unwanted characters and remain with the ones you want. From the expected output, it seems you are still using characters from words tht are 3 characters long:
gsub('\\b(\\pL)\\pL{2,}|.','\\U\\1',sentences,perl = TRUE)
[1] "DPCS" "DSOPGR" "DASP" "DAI"
But if we were to ignore the words you indicated then it would be:
gsub('\\b(\\pL)\\pL{4,}|.','\\U\\1',sentences,perl = TRUE)
[1] "DPCS" "DOGR" "DSP" "DAI"
#Onyambu's answer is great, though as a regular expression beginner, it does take me a long time to try to understand it so that I can make modifications to suit my own needs.
Here is my understanding to gsub('\\b(\\pL)\\pL{4,}|.','\\U\\1',sentences,perl = TRUE).
Post in the hope of being helpful to others.
Background information:
\\b: boundary of word
\\pL matches any kind of letter from any language
{4,} is an occurrence indicator
{m}: The preceding item is matched exactly m times.
{m,}: The preceding item is matched m or more times, i.e., m+
{m,n}: The preceding item is matched at least m times, but not more than n times.
| is OR logic operator
. represents any one character except newline.
\\U\\1 in the replacement text is to reinsert text captured by the pattern as well as capitalize the texts. Note that parentheses () create a numbered capturing group in the pattern.
With all the background knowledge, the interpretation of the command is
replace words matching \\b(\\pL)\\pL{4,} with the first letter
replace any character not matching the above pattern with "" as nothing is captured for this group
Here are two great places I learned all these backgrounds.
https://www.regular-expressions.info/rlanguage.html
https://www3.ntu.edu.sg/home/ehchua/programming/howto/Regexe.html
You can use this pattern: (?<=^| )\S(?=\pL{4,})
I used a positive lookbehind to make sure the matches are preceded by either a space or the beginning of the line. Then I match one character, only if it is followed by 4 or more letters, hence the positive lookahead.
I suggest you don't use \w for non-English languages, because it won't match any characters with accents. Instead, \pL matches any letter from any language.
Once you have your matches, you can just concatenate them to create your strings (dpcs, dogr, etc...)
Here's a demo
I have the following codes
"ABC.A.SVN.10.10.390.10.UDGGL"
"XYZ.Z.SVN.11.12.111.99.ASDDL"
and I need to replace the characters that exist between the 2nd and the 3rd dot. In this case it is SVN but it may well be any combination of between A and ZZZ, so really the only way to make this work is by using the dots.
The required outcome would be:
"ABC.A..10.10.390.10.UDGGL"
"XYZ.Z..11.12.111.99.ASDDL"
I tried variants of grep("^.+(\\.\\).$", "ABC.A.SVN.10.10.390.10.UDGGL") but I get an error.
Some examples of what I have tried with no success :
Link 1
Link 2
EDIT
I tried #Onyambu 's first method and I ran into a variant which I had not accounted for: "ABC.A.AB11.1.12.112.1123.UDGGL". In the replacement part, I also have numeric values. The desired outcome is "ABC.A..1.12.112.1123.UDGGL" and I get it using sub("\\.\\w+.\\B.",".",x) per the second part of his answer!
See code in use here
x <- c("ABC.A.SVN.10.10.390.10.UDGGL", "XYZ.Z.SVN.11.12.111.99.ASDDL")
sub("^(?:[^.]*\\.){2}\\K[^.]*", "", x, perl=T)
^ Assert position at the start of the line
(?:[^.]*\.){2} Match the following exactly twice
[^.]*\. Match any character except . any number of times, followed by .
\K Resets the starting point of the pattern. Any previously consumed characters are no longer included in the final match
[^.]* Match any character except . any number of times
Results in [1] "ABC.A..10.10.390.10.UDGGL" "XYZ.Z..11.12.111.99.ASDDL"
x= "ABC.A.SVN.10.10.390.10.UDGGL" "XYZ.Z.SVN.11.12.111.99.ASDDL"
sub("([A-Z]+)(\\.\\d+)","\\2",x)
[1] "ABC.A..10.10.390.10.UDGGL" "XYZ.Z..11.12.111.99.ASDDL"
([A-Z]+) Capture any word that has the characters A-Z
(\\.\\d+) The captured word above, must be followed with a dot ie\\..This dot is then followed by numbers ie \\d+. This completes the capture.
so far the captured part of the string "ABC.A.SVN.10.10.390.10.UDGGL" is SVN.10 since this is the part that matches the regular expression. But this part was captured as SVN and .10. we do a backreference ie replace the whole SVN.10 with the 2nd part .10
Another logic that will work:
sub("\\.\\w+.\\B.",".",x)
[1] "ABC.A..10.10.390.10.UDGGL" "XYZ.Z..11.12.111.99.ASDDL"
Not exactly regex but here is one more approach
#DATA
S = c("ABC.A.SVN.10.10.390.10.UDGGL", "XYZ.Z.SVN.11.12.111.99.ASDDL")
sapply(X = S,
FUN = function(str){
ind = unlist(gregexpr("\\.", str))[2:3]
paste(c(substring(str, 1, ind[1]),
"SUBSTITUTION",
substring(str, ind[2], )), collapse = "")
},
USE.NAMES = FALSE)
#[1] "ABC.A.SUBSTITUTION.10.10.390.10.UDGGL" "XYZ.Z.SUBSTITUTION.11.12.111.99.ASDDL"