12 Strings

Author
Affiliation

Md Rasel Biswas

IASDS, University of Dhaka

1 Getting started

  • A string is the generic word for character type variables.
  • String operations are particularly useful when cleaning names, survey responses, labels, and other text variables.
  • The stringr package (a core tidyverse package) provides a consistent set of functions for working with strings. Most of its functions begin with str_.
library(tidyverse)

1.1 Creating strings

We can use either single (' ') or double (" ") quotation marks. Both work in the same way.

x <- "R for Data Science"
y <- 'AST230'

x
[1] "R for Data Science"
y
[1] "AST230"
class(x)
[1] "character"

Use quotation marks that make the string easier to write.

"It's time for the lab."
[1] "It's time for the lab."
'I said, "Welcome to AST230!"'
[1] "I said, \"Welcome to AST230!\""

If both types of quotation marks occur in a string, use a backslash (\) to escape the quotation mark that would otherwise end it.

"It's good to say, \"Welcome!\""
[1] "It's good to say, \"Welcome!\""
cat("It's good to say, \"Welcome!\"")
It's good to say, "Welcome!"

Note: R’s printed representation of a string may show escape characters. Use cat() when you want to see how the text actually appears.

1.2 Special characters

A backslash introduces some special characters inside strings.

Code Meaning
\n New line
\t Tab
\\ A backslash
\" A double quotation mark
\' A single quotation mark
cat("First line\nSecond line")
First line
Second line
cat("Name\tRoll")
Name    Roll
cat("C:\\Data\\AST230")
C:\Data\AST230

1.3 Character vectors and built-in constants

A character vector contains one or more strings.

students <- c("Anika", "Rafi", "Nusrat")
students
[1] "Anika"  "Rafi"   "Nusrat"
length(students)
[1] 3

R also provides some ready-made character vectors.

head(letters)
[1] "a" "b" "c" "d" "e" "f"
head(LETTERS)
[1] "A" "B" "C" "D" "E" "F"
head(month.name)
[1] "January"  "February" "March"    "April"    "May"      "June"    
month.abb[1:4]
[1] "Jan" "Feb" "Mar" "Apr"

The stringr package provides examples such as words, sentences, and fruit.

head(words)
[1] "a"        "able"     "about"    "absolute" "accept"   "account" 
head(fruit)
[1] "apple"       "apricot"     "avocado"     "banana"      "bell pepper"
[6] "bilberry"   

2 Basic stringr functions

Here is a summary of the main functions used in this lecture.

Task Functions
Change case str_to_lower(), str_to_upper(), str_to_title()
Measure and extract str_length(), str_sub(), str_locate()
Clean or format str_trim(), str_squish(), str_pad(), str_dup()
Combine or split str_c(), str_split(), word()
Sort str_sort()
Find matches str_detect(), str_count()
Replace matches str_replace(), str_replace_all()

We will learn these functions using simple examples before applying them to data frames.

3 Changing and measuring strings

3.1 Change the case

The following functions convert strings to lower case, upper case, or title case.

x <- "r for DATA science"

str_to_lower(x)
[1] "r for data science"
str_to_upper(x)
[1] "R FOR DATA SCIENCE"
str_to_title(x)
[1] "R For Data Science"

These functions also work on vectors.

names <- c("anika rahman", "RAFI AHMED", "Nusrat jahan")
str_to_title(names)
[1] "Anika Rahman" "Rafi Ahmed"   "Nusrat Jahan"

Note: str_to_title() capitalises the beginning of each word. It does not know whether a word is an acronym or how a particular person’s name should be capitalised.

3.2 Count characters

Do not confuse length() with str_length():

x <- "Data Science"

length(x)      # Number of elements in the vector
[1] 1
str_length(x)  # Number of characters in the string
[1] 12

Spaces are characters, but an empty string has zero characters.

str_length("AST230")
[1] 6
str_length(" ")
[1] 1
str_length("")
[1] 0
str_length(c("R", "AST230", "Statistics"))
[1]  1  6 10

3.3 Get substrings with str_sub()

str_sub() extracts a part of a string using its starting and ending positions. Positions start from 1.

course <- "AST230"

str_sub(course, 1, 3)   # First three characters
[1] "AST"
str_sub(course, 4, 6)   # Characters 4 to 6
[1] "230"
str_sub(course, -3, -1) # Last three characters
[1] "230"

Negative positions count backwards from the end. If the requested end is beyond the string, R returns the available characters.

str_sub("AST230", 1, 20)
[1] "AST230"

We can also replace characters at selected positions.

x <- "ast230"
str_sub(x, 1, 3) <- "AST"
x
[1] "AST230"

3.4 Find location of match with str_locate()

Sometimes we know the word to extract but not its position. str_locate() gives the starting and ending positions of the first match.

x <- "Welcome to the AST230 lab"

position <- str_locate(x, "AST230")
position
     start end
[1,]    16  21
str_sub(x, position[1, "start"], position[1, "end"])
[1] "AST230"

If there is no match, the returned positions are NA.

3.5 Exercise 1: case, length, and extraction

  1. Create x <- "Institute of Applied Statistics and Data Science". Find the number of characters in x.
  2. Convert "uNIVERSITY OF dHAKA" to lower case and to title case.
  3. From "AST230", extract "AST" and "230" separately.
  4. Extract the last four characters from "Bangladesh".
  5. Find the starting and ending positions of "Data" in "R for Data Science".

4 Cleaning and formatting strings

4.1 Remove whitespace

str_trim() removes spaces at the beginning and end of a string.

x <- "   Dhaka   "

str_trim(x)
[1] "Dhaka"
str_trim(x, side = "left")
[1] "Dhaka   "
str_trim(x, side = "right")
[1] "   Dhaka"

str_squish() goes one step further: it also reduces repeated internal whitespace to a single space.

names <- c("  Anika  Rahman ", " Rafi   Ahmed ", "Nusrat Jahan")

str_trim(names)
[1] "Anika  Rahman" "Rafi   Ahmed"  "Nusrat Jahan" 
str_squish(names)
[1] "Anika Rahman" "Rafi Ahmed"   "Nusrat Jahan"

A common cleaning task is to combine these with a case-conversion function.

raw_names <- c("  anika  RAHMAN", "RAFI   ahmed ")

clean_names <- str_to_title(str_squish(raw_names))
clean_names
[1] "Anika Rahman" "Rafi Ahmed"  

4.2 Pad a string to minimum width

str_pad() adds characters until a string reaches a chosen total width.

str_pad("R", width = 5)
[1] "    R"
str_pad("R", width = 5, side = "right")
[1] "R    "
str_pad("R", width = 5, side = "both", pad = "-")
[1] "--R--"

This is useful when creating consistently formatted roll or ID numbers.

roll <- c("7", "24", "105")
str_pad(roll, width = 3, pad = "0")
[1] "007" "024" "105"

str_pad() does not shorten a string if it already exceeds the requested width.

str_pad("AST230", width = 3)
[1] "AST230"

4.3 Duplicate a string

str_dup() joins repeated copies into a single string, whereas rep() repeats elements in a vector.

str_dup("R", 4)
[1] "RRRR"
rep("R", 4)
[1] "R" "R" "R" "R"

4.4 Exercise 2: cleaning student records

student_names <- c("  aNika   rahman ", "RAFI  ahmed", " nusrat JAHAN  ")
rolls <- c("3", "18", "102")
  1. Remove unnecessary spaces from each student name.
  2. Convert the cleaned names to title case.
  3. Format all roll numbers to have three digits (for example, "003").
  4. Find the number of characters in each cleaned student name.

5 Combining and splitting strings

5.1 Combine strings with str_c()

Use str_c() to join strings. The sep argument specifies what appears between arguments.

str_c("AST", "230")
[1] "AST230"
str_c("AST", "230", sep = "-")
[1] "AST-230"
str_c("R", "for", "Data", "Science", sep = " ")
[1] "R for Data Science"

If you have vectors, str_c() combines the corresponding elements.

first_name <- c("Anika", "Rafi", "Nusrat")
last_name <- c("Rahman", "Ahmed", "Jahan")

str_c(first_name, last_name, sep = " ")
[1] "Anika Rahman" "Rafi Ahmed"   "Nusrat Jahan"

Use collapse when you want to combine all elements of a vector into one string.

courses <- c("AST230", "AST305", "PSY206")

str_c(courses, collapse = ", ")
[1] "AST230, AST305, PSY206"
str_c(letters[1:5], collapse = "-")
[1] "a-b-c-d-e"

Remember: sep separates arguments element by element; collapse joins the elements of the result into one string.

5.2 Split strings with str_split()

str_split() divides text wherever it finds the specified separator.

course_label <- "AST_230_Lab_2026"

str_split(course_label, "_")
[[1]]
[1] "AST"  "230"  "Lab"  "2026"

str_split() returns a list of character vectors. For one input string, use [[1]] to get the vector inside the first list element.

parts <- str_split(course_label, "_")[[1]]
parts
[1] "AST"  "230"  "Lab"  "2026"
parts[1]
[1] "AST"
parts[2]
[1] "230"

With several input strings, the result has one list element for each string.

labels <- c("AST-230-Lab", "AST-305-Theory", "PSY-206-Lab")
str_split(labels, "-")
[[1]]
[1] "AST" "230" "Lab"

[[2]]
[1] "AST"    "305"    "Theory"

[[3]]
[1] "PSY" "206" "Lab"

Splitting on "" returns individual characters.

str_split("AST230", "")[[1]]
[1] "A" "S" "T" "2" "3" "0"

5.3 Extract words from a sentence

word() extracts words by position. A negative position counts from the end.

sentence <- "Institute of Applied Statistics and Data Science"

word(sentence, 1)      # First word
[1] "Institute"
word(sentence, 3)      # Third word
[1] "Applied"
word(sentence, -1)     # Last word
[1] "Science"
word(sentence, 3, 4)   # Third through fourth words
[1] "Applied Statistics"

5.4 Exercise 3: course labels

courses <- c("AST230", "AST305", "PSY206", "CSE331")
label <- "IASDS_University_of_Dhaka"
rolls <- c("3", "18", "102")
  1. Combine the course codes into one string, separated by " | ".
  2. Use str_split() to turn label into separate words.
  3. Extract "University" from the split result.
  4. Create "Course: AST230", "Course: AST305", etc., using one str_c() call.
  5. Use str_pad() and str_c() to create the IDs "AST230-003", "AST230-018", and "AST230-102".
  6. Find the last word in "R for Data Science" using word().

6 Sorting and comparing strings

6.1 Sort a vector alphabetically

str_sort() sorts the values.

districts <- c("Sylhet", "Dhaka", "Rajshahi", "Barishal")

str_sort(districts)
[1] "Barishal" "Dhaka"    "Rajshahi" "Sylhet"  
str_sort(districts, decreasing = TRUE)
[1] "Sylhet"   "Rajshahi" "Dhaka"    "Barishal"

6.2 Comparing strings

R is case-sensitive. Extra spaces also affect equality comparisons.

"Dhaka" == "dhaka"
[1] FALSE
"Dhaka" == " Dhaka "
[1] FALSE

Clean the strings before comparing when case or accidental spaces should not matter.

a <- "  DHAKA "
b <- "dhaka"

str_to_lower(str_trim(a)) == str_to_lower(str_trim(b))
[1] TRUE

7 Finding patterns in strings

A pattern is the text we want to find. str_detect() tests whether the pattern occurs within each string and returns TRUE or FALSE.

7.1 Detect a match

courses <- c("AST230", "AST305", "PSY206", "CSE331")

str_detect(courses, "AST")
[1]  TRUE  TRUE FALSE FALSE
str_detect(courses, "230")
[1]  TRUE FALSE FALSE FALSE

For case-insensitive searching, convert the text to a common case first.

responses <- c("Agree", "agree", "DISAGREE", "Neutral")
str_detect(str_to_lower(responses), "agree")
[1]  TRUE  TRUE  TRUE FALSE

Here, "agree" also occurs inside "disagree". If we mean exactly "agree", we need a more specific pattern.

7.2 Match the beginning or end

Two useful pattern symbols are:

  • ^ means start of string.
  • $ means end of string.
courses <- c("AST230", "AST305", "PSY206", "CSE331", "AST230-Lab")

str_detect(courses, "^AST")  # Starts with AST
[1]  TRUE  TRUE FALSE FALSE  TRUE
str_detect(courses, "230$")  # Ends with 230
[1]  TRUE FALSE FALSE FALSE FALSE

Use both symbols to match a whole string rather than just part of it.

responses <- c("Agree", "Disagree", "Agree strongly", "agree")

str_detect(str_to_lower(responses), "agree")
[1] TRUE TRUE TRUE TRUE
str_detect(str_to_lower(responses), "^agree$")
[1]  TRUE FALSE FALSE  TRUE

These start/end symbols are basic examples of regular expressions. We will not cover more complex regular expressions in this lecture.

7.3 Count matches

Because logical values act like 1 (TRUE) and 0 (FALSE) in calculations, we can use sum() and mean() to find the number and proportion of strings containing a pattern.

districts <- c("Dhaka", "Gazipur", "Dhaka", "Sylhet", "Dhaka")

matches <- str_detect(districts, "^Dhaka$")
matches
[1]  TRUE FALSE  TRUE FALSE  TRUE
sum(matches)   # Number of matching observations
[1] 3
mean(matches)  # Proportion of matching observations
[1] 0.6

To count how many times a pattern occurs within each string, use str_count().

str_count(c("banana", "papaya", "apple"), "a")
[1] 3 3 1

If the input contains missing values, str_detect() returns NA for those elements. Use na.rm = TRUE when appropriate.

x <- c("Dhaka", NA, "Gazipur")

str_detect(x, "a")
[1] TRUE   NA TRUE
sum(str_detect(x, "a"), na.rm = TRUE)
[1] 2

7.4 Exercise 4: matching survey responses

responses <- c("Agree", "Disagree", "Neutral", "agree", "Strongly agree", "Agree")
  1. Find the responses that contain "agree", ignoring case.
  2. Find the responses that are exactly "Agree", ignoring case.
  3. Count the number and proportion of exact "Agree" responses.
  4. Find which of the following course codes start with "AST": "AST230", "PSY206", "AST305", and "CSE331".

8 Replacing text

str_replace() replaces the first match in each string; str_replace_all() replaces every match.

x <- c("banana", "papaya", "apple")

str_replace(x, "a", "-")
[1] "b-nana" "p-paya" "-pple" 
str_replace_all(x, "a", "-")
[1] "b-n-n-" "p-p-y-" "-pple" 

This is useful when standardising labels in data.

labels <- c("AST230_Lab", "AST305_Theory", "PSY206_Lab")

str_replace_all(labels, "_", "-")
[1] "AST230-Lab"    "AST305-Theory" "PSY206-Lab"   

Note: As with other R functions, the original object remains unchanged unless you assign the result.

labels <- str_replace_all(labels, "_", "-")
labels
[1] "AST230-Lab"    "AST305-Theory" "PSY206-Lab"   

8.1 Exercise 5: standardising text

items <- c("Data-Science", "R-Programming", "Statistical-Computing")
  1. Replace the first hyphen in each string with a space.
  2. Replace all hyphens with spaces.
  3. Convert the resulting strings to lower case.

9 Working with character columns

Most stringr functions are vectorised: they can process an entire character vector without a loop. This also makes them convenient inside dplyr::mutate().

We will create a small example data frame representing student information.

library(dplyr)

students <- tibble::tibble(
  name = c("  anika RAHMAN", "RAFI  ahmed ", "nusrat jahan"),
  roll = c("7", "24", "105"),
  course = c("AST230", "AST230", "AST305")
)

students
# A tibble: 3 × 3
  name             roll  course
  <chr>            <chr> <chr> 
1 "  anika RAHMAN" 7     AST230
2 "RAFI  ahmed "   24    AST230
3 "nusrat jahan"   105   AST305

Clean the names and make a consistent student ID.

students_clean <- students |>
  mutate(
    name = str_to_title(str_squish(name)),
    student_id = str_c(course, str_pad(roll, 3, pad = "0"), sep = "-")
  )

students_clean
# A tibble: 3 × 4
  name         roll  course student_id
  <chr>        <chr> <chr>  <chr>     
1 Anika Rahman 7     AST230 AST230-007
2 Rafi Ahmed   24    AST230 AST230-024
3 Nusrat Jahan 105   AST305 AST305-105

We can also use str_detect() with filter() to keep rows based on character data.

students_clean |>
  filter(str_detect(course, "^AST230$"))
# A tibble: 2 × 4
  name         roll  course student_id
  <chr>        <chr> <chr>  <chr>     
1 Anika Rahman 7     AST230 AST230-007
2 Rafi Ahmed   24    AST230 AST230-024

9.1 Exercise 6: a small survey dataset

survey <- tibble::tibble(
  id = c("1", "2", "3", "4", "5"),
  district = c(" dhaka", "GAZIPUR ", "Dhaka", " sylhet  ", "gazipur"),
  response = c("Agree", "agree ", "Disagree", "Neutral", "AGREE")
)

survey
# A tibble: 5 × 3
  id    district    response  
  <chr> <chr>       <chr>     
1 1     " dhaka"    "Agree"   
2 2     "GAZIPUR "  "agree "  
3 3     "Dhaka"     "Disagree"
4 4     " sylhet  " "Neutral" 
5 5     "gazipur"   "AGREE"   
  1. Create district_clean by removing extra spaces and converting the district names to title case.
  2. Create response_clean by removing extra spaces and converting responses to title case.
  3. Create an ID with the prefix "S-" and three-digit numbers, such as "S-001".
  4. Keep only the rows where district_clean is "Dhaka".
  5. Calculate the number and proportion of responses that are exactly "Agree" after cleaning.

10 Summary

If you want to… Use…
Change character case str_to_lower(), str_to_upper(), str_to_title()
Count characters or extract part of a string str_length(), str_sub()
Find the location of a substring str_locate()
Remove unnecessary spaces str_trim(), str_squish()
Format IDs with a fixed width str_pad()
Join strings or split them str_c(), str_split()
Extract words word()
Sort strings str_sort()
Test or count matches str_detect(), str_count()
Replace text str_replace(), str_replace_all()

11 References