knitr::include_graphics("https://pbs.twimg.com/profile_images/1051755261583601666/roHaL2s1_400x400.jpg")
knitr::include_graphics("https://pbs.twimg.com/media/EsgTJ19XYAA99xD?format=jpg&name=240x240")
## Introduction
To include a literal single or double quote in a string you can use\
str_c("x","y")
## [1] "xy"
knitr::include_graphics("C:/Users/user/Desktop/advancing knowledge/Programming with R/my first videos/my picture.PNG")


Use the sep argument to control how they’re separated:
str_c("x","y", "z", sep = ",")
## [1] "x,y,z"
To combine multiple variables containing strings data
x<- c("a","b","c")
y<- c("d","e","f")
str_c(x,y)
## [1] "ad" "be" "cf"
Like most other functions in R, missing values are contagious. If you want them to print as “NA”, use str_replace_na()
x <- c("abc",NA)
str_c("|-",x,"-|")
## [1] "|-abc-|" NA
str_c("|-",str_replace_na(x),"-|")
## [1] "|-abc-|" "|-NA-|"
As shown above, str_c() is vectorised, and it automatically recycles shorter vectros to the same length as the longest:
str_c("prefix-",c("a","b","c"),"-suffix")
## [1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"
Objects of length 0 are silently dropped. This is particularly useful in conjunction with if:
name <- "Birasa"
time_of_day <- "morning"
birthday <- FALSE
str_c (
"Good ", time_of_day," ", name,
if(birthday) " and HAPPY BIRTHDAY",
"."
)
## [1] "Good morning Birasa."
To collapse a vector of strings into a single string, use collapse:
str_c(c("x","y", "z"), collapse = ",")
## [1] "x,y,z"
You can extract parts of a string using str_sub(). As well as the string, str_sub() takes start and end arguments which give the (inclusive) positions of the substring:
x <- c("Apple","Banana","Pear")
str_sub(x,1,3)
## [1] "App" "Ban" "Pea"
Note that str_sub() won’t fail if the string is too short it will just return as much as possible:
str_sub("a",1,5)
## [1] "a"
You can also use the assignment form of str_sub() to modify strings:
str_sub(x,1,1) <- str_to_lower(str_sub(x,1,1))
x
## [1] "apple" "banana" "pear"
In code that doesn’t use stringr, you’ll often see paste() and paste0(). What’s the difference between the two functions? what stringr function are they equivalent to? How do the functions differ in their handling of NA?
In your own words, describe the difference between the sep and collapse arguments to str_c().
Use str_length() and str_sub() to extract the middle character from a string. what will you do if the string has an even number of characters?
What does str_wrap() do? when might you want to use it?
What does str_trim() do? what’s the opposite of str_trim()?
Write a function that turns (e.g.) a vector c("a","b","c") into the string a,b, and c. Think carefully about what it should do if given a vector of length 0,10, or 2.
Regexps are a very terse language that allow you to describe patters in strings. They take a little while to get your head around, but once understand them, you’ll find them extremely useful.
To learn regular expressions, we’ll use str_view() and str_view_all(). these functions take a character vector and a regular expression, and show you how they match. we’ll start with very simple regular expressions and then gradually get more and more complicated. Once you’ve mastered pattern matching, you’ll learn how to apply those ideas with various stringr functions.
the simplest patterns match exact strings:
x <- c("apple","banana","pear")
str_view(x,"an")
the next step up in complexity is ., which matches any character (except a newline):
str_view(x,".a.")
But if “.” matches any character, how do you match the character “.”? YOu need to use an “escape” to tell the regular expression you want to match it exactly, not use its special behaviour. Like strings, regexps use the backslash, \, to escape special behaviour. So to match an ., you need to regexp \.. Unfortunately this creates a problem. We use strings to represent regular expressions, and \ is also used as an escape symbol in strings. So to reate the regular expression \. we need the string \\.
# To create the regular expression we need \\
dot <- "\\."
# But the expression itself only contains one:
writeLines(dot)
## \.
# And this tells R to look for an explicit.
str_view(c("abc","a.c","bef"),"a\\.c")
If \ is used as an escape character in regular expressions, how do you match a literal \? Well you need to escape it, creating the regular expression \\. To create that regular expression, you need to use a string, which also needs to escape . That means to match a literal you need to write “\\\\” — you need four backslashes to match one!
x <- "a\\b"
writeLines(x)
## a\b
str_view(x,"\\\\")
Explain why each of these strings don’t match a \: ,\.\.
How would you match the sequence "'\?
What patterns will the regular expression \..\..\... match? How would you represent it as a string?
x <- "a\\..\\..\\..b"
writeLines(x)
## a\..\..\..b
str_view(x,"\\\\..\\\\..\\\\..")
By default, regular expressions will match any part of a string. It’s often useful to anchor the regular expression so that it matches from the start or end of the string. You can use:
^ to match the start of the string.$ to match the end of the string.x <- c("apple","banana","pear")
str_view(x,"^a")
str_view(x,"a$")
To force a regular expression to only match a complete string, anchor it with both ^ and $:
x <- c("apple pie","apple","apple cake")
str_view(x,"apple")
str_view(x,"^apple$")
"$^$"?match argument to str_view() to show only the matching or non-matching words.# 1
v<- c("bira$^$sa","Fabrice","Nyirimana")
writeLines(v)
str_view(v,".[$^$$].")
#2.
## finding words which start by "y"
str_view(words,"^y", match = TRUE)
## finding words which end by "y"
str_view(words,"y$", match = TRUE)
## ## finding words which are exactly three letters
str_view(words,"^...$", match = TRUE)
## finding words which have seven letters and more
str_view(words,".......", match = TRUE)
there are a number of special patterns that match more than one character. You’ve already seen ., which matches any character apart from a newline. There are four other useful tools:
\d: matches any digit.\s: matches any whitespace (e.g. space, tab, newline). *[^abc]: matches anything except a,b, or c.Remember, to create a regular expression containing \d or \s, you’ll need to escape the \ for the string, so you’ll type \\d or \s
A character class containing a single character is a nice alternative to backslash escapes when you want to include a single metacharacter in a regex. Many people find this more readable.
# Look for a literal character that normally has special meaning in a regex
str_view(c("abc", "a.c", "a*c","in$^$ka", "a c"), ".[$.^$].")
str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c")
str_view(c("abc", "a.c", "a*c", "a c"), "a ")
str_view(c("grey", "gray"), "gr(e|a)y")
Create regular expressions to find all words that: 1. start with a vowel. 2. that only contain consonants. (Hint: thinking about matching “not”-vowels.) 3. End with ed, but not with eed. 4. End with ing or ise.
Empirically verify the rule “i before e except after c”.
Is “q” always followed by a “u”?
Write a regular expression that matches a word if it’s probably written in British English, not American English.
Create a regular expression that will match telephone numbers as commonly written in your country.
# 1.
## 1 Vowel starting
str_view(c("Birasa","angel","iabraham","incorrect","gmail"), "^[aiuoe]", match = TRUE)
## 2 only contain consonants.
str_view(c("Birasa","angel","FLY","incorrect","gmail"), "[aiuoe]", match = FALSE)
## 3. strings that start with or end with ed, but not with eed
str_view(c("Saeed","Seed","Fed","Fred","education"), "^ed|[^e]ed$")
## 4 end with ing or ise
str_view(c("Seeing","Seeding","Fedise","Freding","education"), "(ing|ise)$", match = TRUE)
# 2 Empirically verify the rule "i before e except after c"
str_view(c("Saeid","Siejk","Fed","Fred","educieation"),"[^c]ie|cei")
# Empirically verify the rule "i before e except after c"
str_view(words, "ie|cei", match = TRUE)
# Is "q" always followed by a "u"
str_view(words,"(qu|uq)",match = TRUE)
g<- c("queque","euqrope","eunice","oecumenisme","euclesiastica")
str_view(g,"(qu|uq)+",match = TRUE)
#4. Write a regular expression that matches a word if it's probably written in British English, not American English
str_view(words,"ou|ise$|ae|oe|yse$", match = TRUE)
# 5. Create a regular expression that will match telephone numbers as commonly written in your country.
phones<-c("(250) 787564662","(+250) 725114672", "(+250) 786375547","123-456-7890", "(123)456-7890", "(123) 456-7890", "1235-2351")
# the following function read in the phone numbers with RWandan Format and similar (first format)
str_view(phones,"\\(\\d\\d\\d\\)\\s*\\d\\d\\d\\d\\d\\d\\d\\d\\d")
# the following function read in the phone numbers with RWandan Format and similar (second and third format)
str_view(phones,"\\(\\+\\d\\d\\d\\)\\s*\\d\\d\\d\\d\\d\\d\\d\\d\\d")
# or you can replace "\\s*" with [ ] to find the literal space, like in the following code
str_view(phones,"\\(\\+\\d\\d\\d\\)[ ]\\d\\d\\d\\d\\d\\d\\d\\d\\d")
# To match the format of the US phone numbers without parantheses, one could go like the following:
str_view(phones,"\\(\\d\\d\\d\\)\\s*\\d\\d\\d-\\d\\d\\d\\d")
# or with parantheses like the following, as mentioned in the above phone number of phones variable:
str_view(phones,"\\d\\d\\d\\d-\\d\\d\\d\\d")
The next step up in power involves controlling how many times a pattern matches: * ?: 0 or 1 * +: 1 or more * *: 0 or more
x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"
str_view(x,"CC?")
str_view(x,"CC+")
str_view(x,'C+[LX]+')
You can also specify the number of matches precisely: * {n}: exactly n * {n,}: n or more * {,m}: at most m * {n,m}: between n and m
str_view(x,'C{2}')
str_view(x,'C{2,}')
### between 2 and 3
str_view(x,'C{2,3}')
By default these matches are “greedy”: they will match the longest string possible. You can make them “lazy”, matching the shortest string possible by putting a ? after them. This is an advanced feature of regular expressions, but it’s useful to know that it exists:
str_view(x,'C{2,3}?')
str_view(x,'C[LX]?')
h<-c("12ga","birasa","ingabire12","digidig")
str_view(h,'d$', match = TRUE)
h<-c("12ga","birasa","ingabire12","digidig")
str_view(h,'\\d+$', match = TRUE)
h<-c("12ga","birasa","ingabire12","digidig")
str_view(h,'^\\d+', match = TRUE)
Create regular expressions to find all words that:
Start with three consonants.
Have three or more vowels in a row.
Have two or more vowel-consonant pairs in a row.
str_view(words,"^[^aieuo]{3}", match = TRUE)
str_view(words,"(?:[aeiou][^aeiou]*){3,}", match = TRUE)
str_view(words,"[a-z]", match = TRUE)
str_view(words,"^.*$", match = TRUE)
The bat of Matt Jones and Matthew's last name is Jones. The pattern ^Matt.*Jones$ will match Matthew's last name is Jones. Why? The pattern says - the string should start with Matt and end with Jones and there can be zero or more characters (any characters) in between them.n <- c("Matthew's last name is Jones","The bat of Matt Jones")
str_view(n,"^Matthew.*Jones$", match = TRUE)
To determine if a character vector matches a pattern, use str_detect(). It returns a logical vector the same length as the input:
x<- c("apple","banana","pear")
str_detect(x,"n")
## [1] FALSE TRUE FALSE
Remember that when you use a logical vector in a numeric context, FALSE becomes 0 and TRUE becomes 1. That makes sum() and mean() useful if you want to answer questions about matches across a larger vector:
# how many common words start with t?
sum(str_detect(words,"^t"))
## [1] 65
# what proportion of common words end with a vowel?
mean(str_detect(words,"[aeiuo]$"))
## [1] 0.2765306
sum(str_detect(words,"[aeiuo]$"))/length(words)
## [1] 0.2765306
When you have complex logical conditions (e.g. match a or b but not c unless d) it’s often easier to combine multiple str_detect() calls with logical operators, rather than trying to create a single regular expression. For example, here are two ways to find all words that don’t contain any vowels:
# find all words containing at least one vowel, and negate
no_vowels_1 <- !str_detect(words,"[aieuo]")
words[no_vowels_1]
## [1] "by" "dry" "fly" "mrs" "try" "why"
# Find all words consisting only of consonants (non-vowels)
no_vowels_2<- str_detect(words,"^[^aeiou]+$")
words[no_vowels_2]
## [1] "by" "dry" "fly" "mrs" "try" "why"
identical(no_vowels_1,no_vowels_2)
## [1] TRUE
The results are identical, but I think the first approach is significantly easier to understand. If your regular expression gets overly complicated, try breaking it up into smaller pieces, giving each piece a name, and then combining the pieces with logical operations.
A common use of str_detect() is to select the elements that match a pattern. You can do this with logical subsetting, or the convenient str_subset() wrapper:
str_subset(words,"x$")
## [1] "box" "sex" "six" "tax"
words[str_detect(words,"x$")]
## [1] "box" "sex" "six" "tax"
Typically, however, your strings will be one column of a data frame, and you’ll want to use filter instead:
df <- tibble(
word = words,
i = seq_along(word)
)
# the following function will filter the word object for words that end in x and show the results along the their positions the "word" object. i.e: box word has 108th position in the common words.
df %>%
filter(str_detect(word,"x$"))
A variation on str_detect() is str_count(): rather than a simple yes or no, it tells you how many matches there are in a string:
x <- c("apple", "banana", "pear")
mean(str_count(x,"a"))
## [1] 1.666667
# on average, how many vowels per word?
mean(str_count(words,"a"))
## [1] 0.3928571
It’s natural to use str_count() with mutate():
df %>%
mutate(
vowels = str_count(word,"[aeiou]"),
consonants = str_count(word,"[^aeiuo]")
)
str_count(words,"[aeiou]")
## [1] 1 2 3 4 2 3 4 2 1 3 3 1 2 2 4 2 2 2 4 3 3 2 2 2 3 2 1 2 2 2 3 2 2 3 2 4 3
## [38] 1 3 2 1 2 3 3 1 3 3 5 3 3 1 3 3 1 1 1 5 3 1 2 4 5 3 2 2 1 1 1 1 3 1 1 1 2
## [75] 2 1 2 2 3 4 3 1 3 2 2 4 3 1 1 3 1 1 1 1 1 2 2 1 2 2 2 1 2 1 2 2 2 1 1 2 2
## [112] 3 1 3 2 2 2 1 3 1 1 1 0 2 1 1 1 1 2 1 2 1 1 3 1 2 3 2 3 2 2 1 3 2 2 1 1 3
## [149] 3 1 2 1 1 2 1 2 2 2 1 2 2 2 1 3 1 5 2 3 3 2 2 2 4 2 3 2 3 3 3 2 4 2 3 2 2
## [186] 4 2 2 3 2 1 2 2 1 2 3 2 2 2 3 3 2 2 3 1 1 2 1 1 2 2 1 2 2 2 3 3 4 2 4 3 3
## [223] 2 3 2 3 3 2 4 3 2 2 2 2 3 1 2 3 1 2 3 2 1 1 1 1 2 1 0 2 2 2 2 2 2 2 3 4 2
## [260] 1 2 3 2 3 3 2 2 5 1 3 2 2 3 2 4 3 4 4 2 3 2 2 4 2 3 2 3 4 2 2 3 5 3 2 2 2
## [297] 2 1 2 1 2 1 1 1 2 3 2 2 1 2 1 3 2 1 1 2 3 1 2 2 2 1 1 1 2 1 2 0 2 2 2 1 2
## [334] 2 1 3 2 2 2 2 2 2 1 1 1 1 3 1 2 3 2 2 1 3 2 1 1 2 1 1 1 2 3 2 1 1 2 2 2 2
## [371] 1 2 1 2 1 1 1 1 2 1 1 2 2 1 2 2 2 2 2 2 1 1 2 1 2 1 1 3 2 2 2 2 3 1 2 3 1
## [408] 3 2 2 2 3 3 1 4 3 3 1 3 3 4 3 5 2 2 3 3 3 3 2 4 2 3 3 1 2 2 1 2 2 1 1 2 1
## [445] 1 1 1 1 2 1 1 3 1 1 1 4 2 1 2 2 1 1 2 2 3 1 1 1 1 2 2 2 2 1 2 2 2 2 1 1 2
## [482] 2 2 2 2 1 2 1 2 1 2 1 2 1 1 1 3 2 2 2 1 3 1 1 2 1 1 2 1 2 2 3 4 2 2 3 2 1
## [519] 2 1 3 1 3 2 3 1 2 2 2 2 1 2 2 1 2 3 2 0 1 2 1 2 3 3 2 3 2 2 1 1 1 2 1 2 1
## [556] 1 2 2 1 1 2 3 1 2 4 4 1 1 1 2 3 2 2 1 1 2 2 1 2 4 4 3 1 2 4 4 2 4 2 2 2 1
## [593] 1 2 2 2 2 3 2 2 1 1 4 1 1 1 1 2 3 3 1 2 2 2 3 2 3 1 3 3 2 1 1 3 1 2 3 2 3
## [630] 2 4 4 3 1 2 2 3 3 2 1 3 3 1 4 2 1 3 3 2 3 2 3 2 3 2 2 3 2 3 2 1 3 1 1 3 3
## [667] 4 2 2 3 3 3 2 3 2 2 2 2 2 2 4 2 3 4 2 2 4 3 2 1 3 2 2 3 4 3 2 3 4 3 4 2 4
## [704] 1 2 2 1 1 1 2 2 2 1 2 2 2 1 2 2 2 3 2 1 2 2 3 2 2 2 2 3 3 3 2 2 1 1 1 2 4
## [741] 4 2 3 1 2 2 1 1 2 1 2 2 2 1 1 2 1 1 1 2 1 3 2 2 1 2 1 2 1 2 4 1 2 2 1 1 1
## [778] 2 1 3 3 2 1 2 1 1 2 2 2 2 3 3 2 1 1 3 1 2 2 1 2 1 2 3 1 1 1 1 1 1 2 2 2 2
## [815] 1 3 2 1 1 2 2 3 1 2 2 2 2 1 2 1 2 3 2 3 1 1 2 2 1 2 1 2 2 2 4 5 1 1 1 1 3
## [852] 1 1 1 1 1 2 4 1 1 1 3 1 1 2 2 3 2 2 1 2 2 2 1 2 3 3 2 2 1 2 2 2 1 2 2 2 2
## [889] 2 2 2 3 2 1 0 3 1 2 1 1 1 2 3 3 2 3 4 2 2 1 2 2 3 3 4 1 3 2 3 2 2 2 2 1 1
## [926] 1 1 1 1 2 1 2 1 1 2 3 2 2 2 3 1 1 1 1 2 2 1 2 2 1 2 0 2 2 1 1 1 2 1 1 2 3
## [963] 2 2 2 1 1 1 1 2 1 2 2 1 2 1 3 1 2 2
head(words)
## [1] "a" "able" "about" "absolute" "accept" "account"
str_count("abababa","aba")
## [1] 2
str_view_all(c("abababa","aba"),"aba")
Note the use of str_view_all(). As you’ll shortly learn, many stringr functions come in pairs: one function works with a single match, and the other works with all matches. The second function will have the suffix _all.
# Find all words that start or end with x
x<-c("bisericx","xlsx","xls")
x[str_detect(x,"^x.*x$")]
## [1] "xlsx"
# find all words that start with a vowel and end with a consonant
words[str_detect(words,"^[aeiuo].*[^aeiuo]$")]
## [1] "about" "accept" "account" "across" "act"
## [6] "actual" "add" "address" "admit" "affect"
## [11] "afford" "after" "afternoon" "again" "against"
## [16] "agent" "air" "all" "allow" "almost"
## [21] "along" "already" "alright" "although" "always"
## [26] "amount" "and" "another" "answer" "any"
## [31] "apart" "apparent" "appear" "apply" "appoint"
## [36] "approach" "arm" "around" "art" "as"
## [41] "ask" "at" "attend" "authority" "away"
## [46] "awful" "each" "early" "east" "easy"
## [51] "eat" "economy" "effect" "egg" "eight"
## [56] "either" "elect" "electric" "eleven" "employ"
## [61] "end" "english" "enjoy" "enough" "enter"
## [66] "environment" "equal" "especial" "even" "evening"
## [71] "ever" "every" "exact" "except" "exist"
## [76] "expect" "explain" "express" "identify" "if"
## [81] "important" "in" "indeed" "individual" "industry"
## [86] "inform" "instead" "interest" "invest" "it"
## [91] "item" "obvious" "occasion" "odd" "of"
## [96] "off" "offer" "often" "okay" "old"
## [101] "on" "only" "open" "opportunity" "or"
## [106] "order" "original" "other" "ought" "out"
## [111] "over" "own" "under" "understand" "union"
## [116] "unit" "university" "unless" "until" "up"
## [121] "upon" "usual"
#Are there any words that contain at least one of each different vowel?
words[str_count(words,"[aeiuo]")>5]
## character(0)
max(str_count(words,"[aeiou]"))
## [1] 5
# Another function to manipulate strings
# NOT RUN {
s <- "Lorem ipsum dolor sit amet, consectetur adipisicing elit."
stri_count(s, fixed="dolor")
## [1] 1
stri_count(s, regex="\\p{L}+")
## [1] 8
stri_count_fixed(s, " ")
## [1] 7
stri_count_fixed(s, "o")
## [1] 4
stri_count_fixed(s, "it")
## [1] 2
stri_count_fixed(s, letters)
## [1] 2 0 3 2 5 0 1 0 7 0 0 2 3 2 4 2 0 3 4 5 2 0 0 0 0 0
stri_count_fixed("babab", "b")
## [1] 3
stri_count_fixed(c("stringi", "123"), "string")
## [1] 1 0
stri_count_charclass(c("stRRRingi", "STrrrINGI", "123"),
c("\\p{Ll}", "\\p{Lu}", "\\p{Zs}"))
## [1] 6 6 0
stri_count_charclass(" \t\n", "\\p{WHITE_SPACE}") # white space - binary property
## [1] 3
stri_count_charclass(" \t\n", "\\p{Z}") # whitespace - general category (note the difference)
## [1] 1
stri_count_regex(s, "(s|el)it")
## [1] 2
stri_count_regex(s, "i.i")
## [1] 2
stri_count_regex(s, ".it")
## [1] 2
stri_count_regex("bab baab baaab", c("b.*?b", "b.b"))
## [1] 3 2
stri_count_regex(c("stringi", "123"), "^(s|1)")
## [1] 1 1
# }
# transforming a given data
is.vector(words)
## [1] TRUE
more on this topic is found on my stackoverflow account (click here to access it).
x<-c("appropriate","associate","available","colleague","experience","encourage","encouragi","associetu")
x[str_count(x,"[aeiuo]")>4]
## [1] "appropriate" "associate" "available" "colleague" "experience"
## [6] "encourage" "encouragi" "associetu"
# While I wanted to get only: "encouragi" "associetu" which fulfill the criteria of having 5 distinct vowels (i.e: "aeiuo").
# Answers to this question are the following:
x[lengths(lapply(str_extract_all(x,"a|e|i|u|o"), unique)) == 5]
## [1] "encouragi" "associetu"
## Second answer
x[sapply(strsplit(x,""),function(v) sum(unique(v)%in% c("a","e","i","u","o"))>4)]
## [1] "encouragi" "associetu"
## third answer
x[str_count(x,"[a]")>0 & str_count(x,"[e]")>0 & str_count(x,"[i]")>0 & str_count(x,"[o]")>0 & str_count(x,"[u]")>0]
## [1] "encouragi" "associetu"
## Fourth answer
keep <- sapply(x, FUN = function(x){
length(setdiff(c("a", "e", "i", "o", "u"), el(strsplit(x, "")))) == 0
})
x[keep]
## [1] "encouragi" "associetu"