rguides

Working with strings and factors in R: a tutorial

Working with strings and factors in R is a daily reality for anyone doing data analysis. Strings store text data, while factors handle categorical variables that are essential for statistical modeling and data visualization. This tutorial covers everything you need to work with these types effectively.

We’ll start with the basics: creating character vectors, joining and splitting them, and the small but important differences between character and factor columns in a data frame. From there we move to the day-to-day operations, like changing case, padding, trimming, and substituting with regular expressions. The factor section covers how to assign levels explicitly, build ordered factors for ordinal data, and avoid the classic pitfalls of stringsAsFactors and dropped levels. Each section includes a short code block you can run in an interactive R session. For tidyverse-flavored string handling, see the tidyr pivoting tutorial which leans on the same underlying ideas.

What you’ll learn

This tutorial covers the key concepts and practical techniques for working with Working with strings and factors in R. By the end, you will know how to apply the core functions in real data analysis workflows.

Creating strings in r

Strings in R are created using quotation marks. You can use either double quotes (") or single quotes (`’):

# Double quotes
name <- "Alice"
print(name)

# Single quotes
city <- 'London'
print(city)

# String with quotes inside
quote <- "She said \"hello\""
print(quote)

Both approaches work identically in most cases. However, double quotes are more common in R code, especially when working with the tidyverse ecosystem.

Checking string length

The nchar() function returns the number of characters in a string:

text <- "Hello, World!"
nchar(text)
# [1] 13

# Works with vectors too
words <- c("apple", "banana", "cherry")
nchar(words)
# [1] 5 6 6

String manipulation with base r

Base R provides several functions for working with strings. While they’re not as powerful as the stringr package, knowing them helps you understand R’s evolution.

Concatenating strings

Use paste() to combine strings:

first_name <- "John"
last_name <- "Doe"

# Default: separated by space
paste(first_name, last_name)
# [1] "John Doe"

# Custom separator
paste(first_name, last_name, sep = "_")
# [1] "John_Doe"

# paste0: no separator
paste0(first_name, last_name)
# [1] "JohnDoe"

Extracting substrings

The substr() function extracts portions of a string:

text <- "R programming"
substr(text, 1, 9)
# [1] "R program"

# Replace in place
substr(text, 1, 1) <- "r"
text
# [1] "r programming"

Case conversion and pattern matching

Base R provides straightforward functions for changing case — toupper() and tolower() convert entire strings to uppercase or lowercase. For pattern matching, the grep() family covers the essentials: grep() returns the indices of matching elements, grepl() returns a logical vector suitable for filter()-style subsetting, and gsub() replaces all occurrences of a pattern with a replacement string. These functions use regular expressions by default.

text <- "Hello World"
toupper(text)   #> [1] "HELLO WORLD"
tolower(text)   #> [1] "hello world"

# Pattern matching with grep family
colors <- c("red", "blue", "green", "redapple")
grep("red", colors)       #> [1] 1 4  (indices of matches)
grepl("red", colors)      #> [1] TRUE FALSE FALSE TRUE
gsub("red", "RED", colors) #> [1] "RED" "blue" "green" "REDapple"

Introduction to factors

Factors are R’s way of storing categorical data efficiently. They’re stored as integers internally, with corresponding level labels. This makes them memory-efficient and essential for statistical modeling.

Creating a factor

A factor is created from a character vector with factor(). Internally, R stores factors as integers mapped to level labels — this is more memory-efficient than storing repeated strings and lets statistical functions treat the variable as categorical. The class() function confirms the factor type, and levels() returns the unique categories in the order they were detected.

gender <- factor(c("male", "female", "male", "female"))
class(gender)    #> [1] "factor"
levels(gender)   #> [1] "female" "male"

# Levels reflect the unique values in alphabetical order by default
colors <- factor(c("red", "blue", "green", "blue", "red"))
levels(colors)
# [1] "blue" "green" "red"

# Number of levels
nlevels(colors)
# [1] 3

Specifying level order

By default, levels are ordered alphabetically. Use levels argument to control order:

# Default: alphabetical
education <- factor(c("high school", "bachelor", "master", "PhD"))
levels(education)
# [1] "PhD"           "bachelor"      "high school"   "master"

# Custom order (important for modeling and visualization)
education <- factor(c("high school", "bachelor", "master", "PhD"), 
                    levels = c("high school", "bachelor", "master", "PhD"))
levels(education)
# [1] "high school" "bachelor"    "master"      "PhD"

Converting between strings and factors

Understanding how to convert between data types is essential:

String to factor

# Create character vector
colors <- c("red", "blue", "green", "blue", "red")

# Convert to factor
color_factor <- factor(colors)
color_factor
# [1] red   blue  green blue  red  
# Levels: blue green red

Factor to string

# Convert factor back to character
as.character(color_factor)
# [1] "red"  "blue" "green" "blue" "red"

# Important: never use as.numeric() directly on factors!
# This returns internal codes, not the actual values:
as.numeric(color_factor)
# [1] 3 1 2 1 3 (these are level positions, not values!)

# Correct approach:
as.numeric(as.character(color_factor))
# [1] NA NA NA NA NA (because "red" can't become numeric)

# What you usually want:
as.character(color_factor)
# [1] "red"  "blue" "green" "blue" "red"

Working with factors in data frames

When reading data with read.csv() or read.table(), R automatically converts string columns to factors. You can control this behavior:

# Prevent automatic factor conversion
df <- read.csv("data.csv", stringsAsFactors = FALSE)

# Or convert specific columns later
df$category <- factor(df$category)

Modifying factor levels

Replace or consolidate levels after creation:

# Create sample data
survey <- factor(c("yes", "no", "yes", "maybe", "yes"))

# Rename levels
levels(survey) <- c("No", "Yes", "Unsure")
survey
# [1] Yes  No  Yes Unsure Yes 
# Levels: No Yes Unsure

# Combine levels
survey2 <- factor(c("low", "medium", "high", "medium", "low"))
levels(survey2) <- list(Low = "low", High = c("medium", "high"))
survey2
# [1] Low  High High High Low 
# Levels: Low High

Best practices for strings and factors

Follow these guidelines for cleaner, more maintainable code:

  1. Use stringsAsFactors = FALSE when reading data (modern R practice)
  2. Create factors with explicit levels when order matters
  3. Use forcats package for advanced factor manipulation (part of tidyverse)
  4. Check data types with class(), is.factor(), and is.character()
  5. Convert factors to characters before any string operations
# Good practice example
library(dplyr)

# Create a tibble with proper factor handling
df <- tibble(
  name = c("Alice", "Bob", "Charlie"),
  department = factor(c("Sales", "Engineering", "Sales"), 
                      levels = c("Sales", "Engineering", "Marketing"))
)

# Reorder for visualization
df %>% 
  mutate(department = forcats::fct_relevel(department, "Engineering", after = 0))

String operations in base R

Base R provides nchar() for length, toupper() and tolower() for case, substr() for extraction, paste() and paste0() for combining, sprintf() for formatting, gsub() for replacement, and strsplit() for splitting. These functions are vectorized and work on character vectors element-wise.

grep() returns indices of matching elements; grepl() returns a logical vector. regexpr() finds the first match position; gregexpr() finds all. regmatches() extracts the matched substrings using the match positions from regexpr() or gregexpr().

Factors

Factors store categorical data with a fixed set of levels. They are implemented as integers with a levels attribute, memory-efficient for repeated categories. factor(x) creates a factor; levels(f) retrieves the levels; nlevels(f) counts them.

Ordered factors (ordered = TRUE) represent categories with a natural order (e.g., low < medium < high). They support comparison operators: f1 < f2 works for ordered factors but not unordered ones. Statistical models treat ordered factors differently, they generate polynomial contrasts rather than dummy variables.

Factor manipulation with forcats

The forcats package provides tools for working with factors. fct_relevel() reorders levels manually. fct_infreq() orders levels by frequency (most frequent first). fct_lump() collapses infrequent levels into an “Other” category. fct_recode() renames levels. fct_rev() reverses level order, useful for flipping axis order in ggplot2.

Converting between types

as.character(f) converts a factor to character strings (the label, not the integer code). as.integer(f) returns the integer codes. as.numeric(as.character(f)) converts a numeric factor to numbers, never use as.numeric(f) directly, which returns codes. droplevels(f) removes unused levels after subsetting.

String formatting

sprintf() formats strings with C-style placeholders: %s for strings, %d for integers, %f for floats, %.2f for 2 decimal places. format(x, digits = 3) rounds to significant figures. formatC(x, format = "e", digits = 2) formats in scientific notation. prettyNum(x, big.mark = ",") adds thousand separators. These functions are vectorized, sprintf("Item %d costs $%.2f", 1:3, c(1.5, 2.0, 2.5)) returns three formatted strings.

String detection and filtering

grepl(pattern, x) returns a logical vector for filtering: x[grepl("^R", x)] keeps elements starting with R. startsWith(x, "R") and endsWith(x, ".md") are faster for literal prefix/suffix checks. agrep(pattern, x) does approximate (fuzzy) matching. For large-scale pattern matching against many strings, stringi::stri_detect_regex() is faster than base R grepl() for long vectors.

Factor levels and ordering

Factor levels define valid values and control ordering. levels(f) returns the levels. nlevels(f) counts them. factor(x, levels = c("low", "medium", "high"), ordered = TRUE) creates an ordered factor where "low" < "medium" < "high". Statistical models treat ordered factors with polynomial contrasts rather than dummy variables. In ggplot2, factor level order controls axis order, legend order, and color assignment.

String operations on factors

Factors are not character vectors, paste(f) converts to character first, then pastes. as.character(f) converts to the label; as.integer(f) returns the integer code. levels(f)[f] also returns labels. relevel(f, ref = "medium") changes the reference level for contrasts in statistical models. droplevels(f) removes levels with zero observations after filtering.

String interpolation and formatting

R has several ways to build strings from variables. The paste() and paste0() functions concatenate values, with paste0() using no separator. For formatted output similar to Python’s f-strings, sprintf() uses C-style format specifiers: %s for strings, %d for integers, %f for floating-point numbers. The glue package provides a more readable alternative where expressions inside curly braces are evaluated inline.

sprintf() is the most precise tool for numeric formatting. sprintf("%.2f", 3.14159) gives "3.14", and sprintf("%05d", 42) gives "00042" with zero-padding. For aligning text in fixed-width output, formatC() gives fine control over width, fill character, and alignment direction.

When building dynamic strings in loops, avoid paste() in a loop accumulating a long string, each call creates a new string object. Collect the pieces in a vector and call paste(pieces, collapse = "") once at the end. For very large text assembly, stringr::str_c() with collapse is equivalent and slightly more consistent about NA handling.

Regular expression fundamentals

Strings and factors both interact with regular expressions for matching and extraction. R supports two regex flavors: POSIX (used by grep() with perl = FALSE) and PCRE (used when perl = TRUE or by stringr, which always uses PCRE via the ICU library).

The most common operations are: grepl(pattern, x) returns a logical vector of matches; grep(pattern, x) returns indices; sub(pattern, replacement, x) replaces the first match; gsub(pattern, replacement, x) replaces all matches. In stringr: str_detect(), str_which(), str_replace(), and str_replace_all() are the equivalents.

Character classes in PCRE include \d (digits), \w (word characters), \s (whitespace), and their uppercase negations (\D, \W, \S). Anchors ^ and $ match the start and end of a string (or line with the (?m) flag). Lookahead (?=...) and lookbehind (?<=...) match positions without consuming characters.

When a regex pattern contains backslashes, R requires double escaping because the string parser consumes one backslash before the regex engine sees it. The pattern \d+ in R source code becomes \d+ in the regex engine. The raw_string() function introduced in R 4.0 (via r"(\d+)" syntax) lets you write regex without extra escaping.

Factor internals and ordering

Factors store data as integers with a levels attribute, a character vector. as.integer(f) returns the underlying codes; levels(f) returns the levels. This integer encoding is what makes factors memory-efficient for repeated strings: a column with a million rows containing one of five city names stores a million integers and five strings, not a million strings.

Ordered factors carry additional information about the direction of the ordering. factor(c("low", "med", "high"), levels = c("low", "med", "high"), ordered = TRUE) creates a factor where "low" < "med" < "high". Ordered factors interact with comparison operators (<, >) and some modeling functions use them differently, lm() and glm() treat ordered factors with polynomial contrasts by default rather than treatment contrasts.

droplevels() removes unused levels from a factor or from all factor columns in a data frame. After filtering a data frame, call droplevels(df) to remove levels that no longer appear in the data. This matters for plotting (empty bars in bar charts) and modeling (zero-variance predictors). In dplyr, filter() does not drop levels automatically; use forcats::fct_drop() or pipe through droplevels().

When reading data from CSV files, old R code frequently assumes stringsAsFactors = TRUE (the pre-4.0 default). If you inherit such code, columns you expect to be character are actually factors. Calling mutate(across(where(is.factor), as.character)) in a dplyr pipeline converts all factor columns to character in one step.

The reverse path, coercing character to factor, is useful before modeling or plotting by category. forcats::fct_infreq() orders levels by frequency (most common first), which produces sensible bar chart ordering without manual specification. forcats::fct_reorder(f, x) reorders levels by the median (or another summary) of a numeric variable x, useful for reordering a categorical axis so bars appear in ascending order.

Working with text data in practice

Text data analysis starts with understanding what you have. unique(x) shows distinct values; nchar(x) measures lengths; table(substr(x, 1, 3)) examines prefixes. These exploratory steps reveal encoding issues, inconsistent formatting, and unexpected values before you write cleaning code.

For data cleaning pipelines, the key insight is that string operations are fast and vectorized. A single gsub() call on a million-row column runs in milliseconds. The bottleneck is almost never string operations themselves but the downstream steps, sorting, joining, aggregating.

Factors add structure to string data when the distinct values are known and finite. Use factors for country codes, product categories, severity levels, and similar categorical data. Avoid factors for free-text fields, identifiers, or any column where the distinct values are not a closed set known in advance.

The interaction between strings and factors in tidyverse code is well-defined. readr reads character columns as character by default and never creates factors automatically. dplyr operations preserve factors when the output levels are a subset of the input levels. ggplot2 uses factor level order for axis ordering and legend ordering. Understanding these behaviors prevents surprising results in data analysis pipelines.

String encoding issues surface when working with international text, web-scraped data, or files from systems that use non-UTF-8 encodings. Encoding(x) returns the encoding of each string. enc2utf8(x) converts to UTF-8. iconv(x, from = "latin1", to = "UTF-8") converts between specific encodings. When readr::read_csv() misreads characters, the locale argument with a specific encoding solves it.

For high-cardinality string columns (millions of distinct values), character vectors are efficient in R because strings are interned in a global string pool, duplicate strings share memory. object.size() quantifies the memory of any R object. A character vector with many repeated values uses less memory than you might expect because each unique string is stored once.

String encoding and unicode

R stores strings as UTF-8 internally (since R 4.0 on most platforms). chartr("aeiou", "AEIOU", x) translates individual characters. nchar(x) counts Unicode code points, not bytes. nchar(x, type = "bytes") counts bytes, which differs for multi-byte characters.

Encoding(x) returns the declared encoding: "UTF-8", "latin1", "bytes", or "unknown". enc2utf8(x) converts to UTF-8. enc2native(x) converts to the native encoding. iconv(x, from = "UTF-8", to = "ASCII//TRANSLIT") transliterates non-ASCII characters to their closest ASCII equivalents, useful for generating file names from text.

stringi::stri_detect_charclass(x, "[\p{Script=Arabic}]") uses ICU character classes to detect text in specific scripts. Unicode properties (\p{L} for any letter, \p{Lu} for uppercase, \p{N} for any number) are available in all ICU-based functions including stringr.

Factors in statistical models

Factor variables interact with regression models in specific ways. lm(y ~ f) with a factor f creates indicator variables (dummy variables) for each level except the reference level. The reference level is the first level (alphabetically, or the first in the specified level order).

contrasts(f) shows the contrast matrix. contr.treatment(levels(f)) is the default treatment coding (reference level = zero). contr.sum(levels(f)) uses sum-to-zero coding (effects sum to zero). contr.helmert() uses Helmert coding. Change contrasts with contrasts(f) <- contr.sum(nlevels(f)).

Ordered factors use polynomial contrasts by default in lm() and glm(). These model linear, quadratic, and cubic trends across ordered levels. To use simple treatment contrasts for ordered factors, set options(contrasts = c("contr.treatment", "contr.treatment")) or specify contrasts(f) <- contr.treatment(nlevels(f)).

Performance with large string data

For large datasets with many repeated string values, factors save memory because the strings are stored once as levels and the data column stores small integers. A column with one million rows and five distinct city names stores 5 strings as levels and one million 4-byte integers, much less than one million variable-length strings.

object.size(character_col) versus object.size(as.factor(character_col)) shows the memory difference. For highly repeated strings (low cardinality), the factor representation is 5-10x smaller.

String operations are generally fast in R because strings are interned, duplicate strings share the same memory. However, paste() in a loop that accumulates a growing string is O(n²) because each iteration copies the entire result. Collect strings in a vector and call paste(collapse = "") once.

stringi functions are faster than base R string functions and stringr for bulk operations on large vectors. For processing millions of rows, benchmark stringi::stri_detect_fixed() against grepl(fixed = TRUE) and stringr::str_detect(fixed()).

Conceptual overview: strings in R

Strings in R are character vectors. Every character value is technically a character vector of length one. The simplest operations, comparing two strings, checking their length, combining them, work uniformly on vectors containing one or a million elements without any change to the code.

The tidyverse’s stringr package makes string operations more predictable than base R’s mix of grep, sub, gsub, regexpr, and related functions. The stringr philosophy is that every function should take the string as the first argument, return a consistent type, and handle missing values consistently. With base R string functions, the return type and handling of NAs varies by function, making pipelines brittle.

String operations are computationally inexpensive compared to most data analysis steps. Even on millions of records, string cleaning typically takes milliseconds. The cost you pay is thinking through the logic — what patterns to match, what edge cases exist in the data, whether the regex correctly handles all input variations.

Conceptual overview: factors in R

Factors represent categorical variables with a fixed set of known levels. The key distinction from character vectors is that factors carry information about all possible values, not just the values present in the data. A character vector of survey responses has no concept of responses that were not given; a factor can represent empty categories explicitly.

This property matters for statistical modeling, where you need to know all possible values of a categorical predictor before fitting the model. It matters for visualization, where you want consistent axis ordering and legend entries even when some categories have no data in a particular subset.

The tidyverse’s forcats package handles the most common factor manipulation tasks — reordering by frequency, collapsing rare levels, dropping empty levels — with a consistent naming convention. Functions that change the order of levels start with fct_reorder or fct_relevel. Functions that reduce the number of levels start with fct_collapse or fct_lump. The naming makes the purpose of each function apparent.

Understanding when to use factors versus character vectors is a key R skill. Use character vectors when the set of possible values is open-ended, when you are working with free-text data, or when you are storing identifiers. Use factors when the set of possible values is closed and known, when level ordering matters for analysis or visualization, or when you need to model categorical variables in statistical functions.

The historical reason factors are common in old R code is that they used to be required for efficient storage and for statistical modeling. Before R version 4.0, character columns were automatically converted to factors when reading CSV files. Modern R and the tidyverse have eliminated these constraints, but understanding factors remains important because so much existing R code uses them.

How R stores text internally

Text in R is represented as character vectors, where each element is a sequence of Unicode code points. The language makes no distinction between a single character and a multi-character string — both are length-one character vectors. This uniformity simplifies function signatures but means you must be explicit about whether you want element-wise operations or character-by-character operations.

Character vectors in R are reference-counted. When you assign a string to a new variable, R does not immediately copy the data — both variables point to the same memory. The copy happens only when one of them is modified. For large string datasets, this copy-on-modify behavior means memory usage stays low until you actually mutate the data.

String literals and special characters

R accepts both single and double quotes for string literals. The two forms are interchangeable except when the string itself contains one type of quote — using the other type as the delimiter avoids backslash escaping. Escape sequences follow standard conventions: newline, tab, literal backslash, and quote characters. Raw strings, introduced in R 4.0, treat backslashes literally and are useful for regular expressions and Windows file paths where double-escaping becomes error-prone.

Measuring string length

Measuring string length in R requires care because length() counts vector elements, not characters. A single string “hello” has a length of 1 but a character count of 5. This distinction matters constantly when writing functions that process text. Always use nchar() when you mean the number of characters in a string, and length() when you mean the number of strings in a vector.

nchar() counts Unicode code points by default. For ASCII text this equals byte count, but for multi-byte characters like emoji or accented letters, type = "bytes" gives the byte count while type = "chars" gives the character count. When validating input lengths for database columns or API fields that have byte limits, always measure bytes.

Concatenation patterns

R has no string concatenation operator. The closest equivalent is paste() with sep = "" or the shorthand paste0(). Both are vectorized — paste0 on two parallel vectors produces a vector of combined strings. The collapse argument controls whether the result is a single string or a vector.

Building strings in loops with repeated paste calls is inefficient because each call allocates a new string. For accumulating many strings, build a character vector and call paste once at the end with collapse. For template-style substitution, sprintf() handles multiple insertions in one call and supports format specifiers for numbers and padding.

Factor levels and memory

Factors store levels as a character vector and encode each observation as an integer index into that vector. A factor with one million observations of fifty unique values stores fifty strings and one million integers — far less memory than one million repeated character strings. This compression makes factors efficient for large categorical data in statistical models.

The levels vector is sorted alphabetically by default. The first level becomes the reference category in regression models, so level ordering has statistical consequences, not just display consequences. Changing the reference level by reordering changes model coefficients and is a routine step in regression analysis. Always check factor level order before fitting a model and set it explicitly rather than relying on alphabetical defaults.

Ordered factors for ranked categories

Ordinal data — size categories, satisfaction ratings, education levels — should use ordered factors rather than unordered ones. An ordered factor supports comparison operators: “low” is less than “medium” which is less than “high” evaluates correctly. Unordered factors treat all levels as equally distant, which is wrong for ordinal data and produces incorrect results in some modeling functions.

Creating ordered factors requires specifying the levels explicitly in the correct order. R does not infer ordering from level names. After creating an ordered factor, verify the ordering before using it in a model, since a mistake in level ordering silently produces wrong results.

Pattern matching fundamentals

Regular expressions are a mini-language for describing patterns in text. In R, string functions that accept patterns use PCRE (Perl-Compatible Regular Expressions) by default. The pattern describes what to match: literal characters match themselves, while metacharacters like dots, stars, and brackets carry special meaning. A dot matches any character. A star means zero or more repetitions of the preceding element. Square brackets define a character class.

Learning regular expressions pays dividends across programming languages because the same patterns work in Python, JavaScript, and most other languages. The differences between implementations are at the margins — lookahead behavior, named capture groups — not in the core syntax. The investment in learning regex is not R-specific.

Character classes and anchors

Character classes match any one character from a specified set. The class [aeiou] matches any lowercase vowel. Negating with a caret inside the brackets — [^0-9] — matches any character that is not a digit. Shorthand classes abbreviate common sets: the uppercase D shorthand matches non-digits, uppercase W matches non-word characters, uppercase S matches non-whitespace.

Anchors constrain where in the string a match can occur. The caret outside of square brackets anchors to the start of the string. The dollar sign anchors to the end. Anchors are essential for validation patterns that must match the entire string rather than any substring. A pattern that checks for five digits without anchors matches strings that contain five consecutive digits anywhere, including strings like “abc12345xyz” that should fail validation.

String splitting and joining

Splitting strings by a separator is a frequent operation when parsing delimited text. The result is a list where each element is a character vector of the pieces from splitting one string. When applying splitting to an entire column, you get a list-column that requires unnesting or further processing. The fixed string form is faster for literal separator characters that have no special regex meaning.

Joining strings reverses the split. Collapsing a character vector into one string with a separator — joining words with spaces, joining IDs with commas — is the complement of splitting. For joining across multiple columns of a data frame row-wise, paste or stringr’s string glue functions combine multiple values per row into a single string in one call.

String padding and alignment

Padding strings to a fixed width is common when generating formatted output, creating fixed-width text files, or aligning values in printed tables. Left-padding with zeros converts a number to a zero-padded string of a specific width — “007” instead of “7”. Right-padding with spaces fills a string to a fixed width for column alignment. Both operations are done with formatC or str_pad from stringr.

When preparing strings for a fixed-width output format, measure the maximum width of each column first, then pad all values in that column to that width. This ensures consistent alignment regardless of value length. For numeric data displayed as strings, right-align with zero-padding. For text data, left-align with space-padding.

Text encoding in practice

Most text data today is UTF-8, and R handles UTF-8 correctly. Problems arise when data comes from sources that use other encodings: Windows systems often produce files in Windows-1252, legacy databases may use Latin-1, and some older data exchange formats use ASCII with special escaping for non-ASCII characters. Reading such files without specifying the encoding produces garbled output — replacement characters or question marks where non-ASCII characters should be.

The iconv function converts between encodings. Converting Windows-1252 to UTF-8 is the most common use case. The Encoding function reports the declared encoding of a string, which may differ from the actual encoding if the declaration is wrong. For strings of unknown origin, trying a conversion and checking whether the result looks correct is often the most practical approach.

String operations at scale

For data frames with millions of rows, string operations can be the performance bottleneck. vectorized stringr operations are faster than for loops but still slower than numeric operations because string memory allocation is expensive. Profiling string-heavy code with profvis or Rprof identifies which operations consume the most time.

Compiling regular expressions once and reusing them is faster than compiling the same pattern on every call. Some R string functions accept pre-compiled patterns; others recompile from the string pattern argument on every call. For code that applies the same pattern repeatedly in a loop, pre-compilation reduces overhead. In most practical analyses the difference is small, but for high-volume text processing it is worth measuring.

Summary

You’ve learned how to create and manipulate strings in R, including concatenation, substring extraction, and pattern matching. You’ve also mastered factors, R’s powerful way of handling categorical data. Key takeaways:

  • Strings are text data created with quotes (" or ')
  • Use paste() for concatenation, nchar() for length
  • grep(), grepl(), and gsub() handle pattern matching
  • Factors store categorical data efficiently as integers with level labels
  • Always specify factor levels explicitly when order matters
  • Convert factors to characters before string operations

These skills form the foundation for text processing and categorical data analysis in R. In the next tutorial, you’ll learn about error handling to make your R code more reliable.

Next steps

Now that you understand working with strings and factors in R, explore these related topics to deepen your knowledge and apply these techniques in more complex scenarios.

See also