R Operators
Learning Objectives
By the end of this tutorial, you will be able to:
- Use all arithmetic operators including modulo and integer division
- Apply comparison operators to create logical vectors
- Combine conditions with logical operators
- Master assignment operators including the pipe operator
- Understand operator precedence in R
- Apply element-wise vs. matrix operations correctly
Arithmetic Operators
| Operator | Description | Example | Result |
|---|---|---|---|
+ | Addition | 5 + 3 | 8 |
- | Subtraction | 5 - 3 | 2 |
* | Multiplication | 5 * 3 | 15 |
/ | Division | 10 / 3 | 3.333333 |
^ or ** | Exponentiation | 2^10 | 1024 |
%% | Modulo (remainder) | 10 %% 3 | 1 |
%/% | Integer division | 10 %/% 3 | 3 |
# Basic arithmetic
10 + 5 # [1] 15
10 - 5 # [1] 5
10 * 5 # [1] 50
10 / 5 # [1] 2
# Exponentiation
2^10 # [1] 1024
9^0.5 # [1] 3 (square root)
# Modulo (remainder)
10 %% 3 # [1] 1
15 %% 5 # [1] 0
# Integer division
10 %/% 3 # [1] 3
15 %/% 5 # [1] 3
Vectorized Arithmetic
R operations work element-wise on vectors:
x <- c(1, 2, 3, 4, 5)
y <- c(10, 20, 30, 40, 50)
x + y # [1] 11 22 33 44 55
x * y # [1] 10 40 90 160 250
x^2 # [1] 1 4 9 16 25
# Scalar recycling
x + 100 # [1] 101 102 103 104 105
# Element-wise vs matrix multiplication
a <- c(1, 2, 3)
b <- c(4, 5, 6)
a * b # [1] 4 10 18 (element-wise)
a %*% b # [1] 32 (matrix dot product)
Comparison Operators
| Operator | Description | Example | Result |
|---|---|---|---|
== | Equal | 5 == 5 | TRUE |
!= | Not equal | 5 != 3 | TRUE |
> | Greater than | 5 > 3 | TRUE |
< | Less than | 5 < 3 | FALSE |
>= | Greater than or equal | 5 >= 5 | TRUE |
<= | Less than or equal | 5 <= 3 | FALSE |
# Basic comparisons
5 == 5 # [1] TRUE
5 != 3 # [1] TRUE
5 > 3 # [1] TRUE
5 < 3 # [1] FALSE
5 >= 5 # [1] TRUE
5 <= 3 # [1] FALSE
# Vectorized comparisons
x <- c(1, 2, 3, 4, 5)
x > 3 # [1] FALSE FALSE FALSE TRUE TRUE
x == 3 # [1] FALSE FALSE TRUE FALSE FALSE
# Comparing vectors
a <- c(1, 2, 3, 4, 5)
b <- c(5, 4, 3, 2, 1)
a == b # [1] FALSE FALSE TRUE FALSE FALSE
a > b # [1] FALSE FALSE FALSE TRUE TRUE
Floating-Point Comparison
# Warning: floating-point precision issues
0.1 + 0.2 == 0.3
# [1] FALSE
# Solution: use all.equal()
all.equal(0.1 + 0.2, 0.3)
# [1] TRUE
# Or use tolerance
abs(0.1 + 0.2 - 0.3) < 1e-10
# [1] TRUE
Logical Operators
Element-Wise Operators
| Operator | Description | Example | Result |
|---|---|---|---|
& | AND | TRUE & FALSE | FALSE |
| | OR | TRUE | FALSE | TRUE |
! | NOT | !TRUE | FALSE |
xor() | Exclusive OR | xor(TRUE, FALSE) | TRUE |
# Logical operations
TRUE & FALSE # [1] FALSE
TRUE | FALSE # [1] TRUE
!TRUE # [1] FALSE
xor(TRUE, FALSE)# [1] TRUE
xor(TRUE, TRUE) # [1] FALSE
# Vectorized logical operations
x <- c(TRUE, TRUE, FALSE, FALSE)
y <- c(TRUE, FALSE, TRUE, FALSE)
x & y # [1] TRUE FALSE FALSE FALSE
x | y # [1] TRUE TRUE TRUE FALSE
!x # [1] FALSE FALSE TRUE TRUE
# Practical example
scores <- c(85, 92, 78, 95, 88)
passed <- scores >= 80
passed
# [1] TRUE TRUE FALSE TRUE TRUE
sum(passed) # [1] 4 (count of TRUE)
mean(passed) # [1] 0.8 (proportion TRUE)
Short-Circuit Operators
&& and || evaluate only the first element of each vector:
# && and || are scalar (short-circuit)
TRUE && FALSE # [1] FALSE
TRUE || FALSE # [1] TRUE
# Short-circuit evaluation
x <- NULL
# This would error: is.null(x) && length(x) == 0
# But short-circuit saves us:
is.null(x) && length(x) == 0 # [1] TRUE (doesn't evaluate second part)
# Useful for validation
validate <- function(x) {
if (is.null(x) || length(x) == 0) {
cat("Input is empty\n")
return(invisible(NULL))
}
cat("Input has", length(x), "elements\n")
}
validate(NULL) # Input is empty
validate(1:5) # Input has 5 elements
Which, Any, All
x <- c(1, 5, 3, 8, 2, 9, 4)
# which() — indices where condition is TRUE
which(x > 5)
# [1] 4 6
# any() — is at least one TRUE?
any(x > 5) # [1] TRUE
any(x > 10) # [1] FALSE
# all() — are all TRUE?
all(x > 0) # [1] TRUE
all(x > 5) # [1] FALSE
Assignment Operators
| Operator | Description | Example |
|---|---|---|
<- | Left assignment (preferred) | x <- 5 |
-> | Right assignment | 5 -> x |
= | Assignment (less common) | x = 5 |
<<- | Global assignment | x <<- 5 |
= (in call) | Named argument | mean(x = c(1,2,3)) |
# Standard assignment
x <- 10
# Right assignment (rare)
10 -> x
# Global assignment (modifies parent environment)
f <- function() {
x <<- 99 # Modifies global x
}
f()
x # [1] 99
Pipe Operators
Base Pipe |> (R 4.1+)
# Without pipe
round(sqrt(mean(c(1, 2, 3, 4, 5))), 2)
# With pipe
c(1, 2, 3, 4, 5) |>
mean() |>
sqrt() |>
round(2)
# Pipe with placeholder
"hello world" |>
toupper() |>
paste("!", sep = "")
# Using _ as placeholder (R 4.2+)
1:10 |>
mean() |>
round(_, 2)
Magrittr Pipe %>% (tidyverse)
library(magrittr)
# Same as |> but available in older R versions
c(1, 2, 3, 4, 5) %>%
mean() %>%
sqrt() %>%
round(2)
# With placeholder .
"hello world" %>%
toupper() %>%
paste("!", sep = "")
Pipe Comparison
| Feature | |> | %>% |
|---|---|---|
| Base R | Yes (4.1+) | No (needs magrittr) |
| Placeholder | _ (4.2+) | . |
| Function extraction | \(x) x + 1 | list(..1) |
| Speed | Slightly faster | Slightly slower |
Matrix Operators
| Operator | Description | Example |
|---|---|---|
%*% | Matrix multiplication | A %*% B |
t() | Transpose | t(A) |
solve() | Inverse | solve(A) |
%o% | Outer product | x %o% y |
%in% | Match operator | "a" %in% c("a","b") |
# Matrix multiplication
A <- matrix(1:4, nrow = 2)
B <- matrix(5:8, nrow = 2)
A %*% B
# [,1] [,2]
# [1,] 19 22
# [2,] 43 50
# Transpose
t(A)
# [,1] [,2]
# [1,] 1 3
# [2,] 2 4
# Inverse
M <- matrix(c(1, 2, 3, 4), nrow = 2)
solve(M)
# [,1] [,2]
# [1,] -2 1.5
# [2,] 1 -0.5
# Outer product
x <- 1:3
y <- 1:4
x %o% y
# [,1] [,2] [,3] [,4]
# [1,] 1 2 3 4
# [2,] 2 4 6 8
# [3,] 3 6 9 12
# Membership
"a" %in% c("a", "b", "c") # [1] TRUE
"z" %in% c("a", "b", "c") # [1] FALSE
Operator Precedence
R evaluates operators in a specific order (highest to lowest):
| Precedence | Operator | Description |
|---|---|---|
| 1 | $, @, [, [[ | Component extraction |
| 2 | ^ | Exponentiation |
| 3 | -, + | Unary minus/plus |
| 4 | : | Sequence generation |
| 5 | %%, %/% | Modulo, integer division |
| 6 | *, / | Multiplication, division |
| 7 | +, - | Addition, subtraction |
| 8 | <, >, <=, >=, ==, != | Comparison |
| 9 | ! | Logical NOT |
| 10 | &, && | AND |
| 11 | |, || | OR |
| 12 | ~ | Formula |
| 13 | ->, ->> | Right assignment |
| 14 | <-, <<- | Left assignment |
| 15 | = | Assignment |
# Precedence matters
2 + 3 * 4 # [1] 14 (not 20)
(2 + 3) * 4 # [1] 20
# Exponentiation is right-associative
2^3^2 # [1] 512 (2^(3^2) = 2^9)
(2^3)^2 # [1] 64
# Use parentheses for clarity
result <- (x + y) * (a - b)
Type Conversion in Operations
# Logical → Numeric
TRUE + TRUE # [1] 2
TRUE * 10 # [1] 10
# Numeric → Character
"Value: " + 42
# Warning: argument is not interpretable as numeric
# [1] NA
# Better approach
paste("Value:", 42) # [1] "Value: 42"
# Character → Numeric (with warning)
as.numeric("42") # [1] 42
as.numeric("hello") # [1] NA with warning
# Integer vs Double
x <- 42L
y <- 42
typeof(x + y) # [1] "double" (promoted)
Practice Exercises
Exercise 1: Vector Operations
Given x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10):
- Find all elements greater than 5
- Count how many elements are even
- Calculate the sum of elements between 3 and 7 (inclusive)
- Find indices of elements divisible by 3
Solution
x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
# 1. Elements greater than 5
x[x > 5]
# [1] 6 7 8 9 10
# 2. Count even elements
sum(x %% 2 == 0)
# [1] 5
# 3. Sum of elements between 3 and 7
sum(x[x >= 3 & x <= 7])
# [1] 25
# 4. Indices divisible by 3
which(x %% 3 == 0)
# [1] 3 6 9
Exercise 2: Logical Conditions
Write expressions that check if a number is:
- Positive
- Between 0 and 100 (inclusive)
- Divisible by both 3 and 5
Solution
is_positive <- function(x) {
x > 0
}
in_range <- function(x) {
x >= 0 & x <= 100
}
divisible_by_3_and_5 <- function(x) {
x %% 3 == 0 & x %% 5 == 0
}
# Test
is_positive(5) # [1] TRUE
in_range(50) # [1] TRUE
divisible_by_3_and_5(15) # [1] TRUE
divisible_by_3_and_5(10) # [1] FALSE
Key Takeaways
- R is vectorized — operators work element-wise on vectors
- Use
%%for modulo,%/%for integer division - Comparison operators return logical vectors (TRUE/FALSE)
&/|are element-wise,&&/||are scalar (short-circuit)%in%is the membership operator — check if value is in a vector%*%is matrix multiplication — not*which is element-wise|>is the base pipe — chain operations cleanly- Operator precedence matters — use parentheses for clarity
- Floating-point comparison — use
all.equal()instead of==
Next: Learn about R Strings — text manipulation in R.