11  Arithmetic, Relational, and Logical Operations on Vectors

NoteWhat This Chapter Covers

This chapter shows how the three families of operators you met in Chapter 7 behave when both sides are vectors. You will see that R applies arithmetic operators element-by-element, that relational operators produce logical vectors of the same length as their inputs, and that logical operators combine those results cleanly for filtering and decision making. You will meet the family of summary functions that collapse a vector into a single number (sum(), mean(), min(), max(), prod(), range(), var(), sd()), the helper functions that count or locate matches (any(), all(), which()), and the vectorised decision-maker ifelse(). You will also see two small patterns that recur through the book: centring and scaling, and filter-then-summarise. By the end of this chapter you will be able to read a column of data and answer most first-pass questions about it in a single line.

flowchart LR
    V["Vectors"] --> A["Arithmetic <br> + - * / ^ %% %/%"]
    V --> R["Relational <br> < <= > >= == !="]
    V --> L["Logical <br> & | ! xor"]
    A --> N["Numeric Vector"]
    R --> LV["Logical Vector"]
    L --> LV
    LV --> S["Summarise <br> sum / mean / any / all / which"]
    LV --> F["Filter <br> x[cond], x[!is.na(x) & ...]"]
    classDef default fill:#004466,color:#ffffff,stroke:#ffcc00,stroke-width:3px,rx:10px,ry:10px;


11.1 Arithmetic on Vectors

NoteElement-by-Element by Default

Every arithmetic operator acts on each pair of corresponding elements. Adding two length-5 vectors produces a length-5 result.

NoteScalar-and-Vector: Recycling in Its Most Useful Form

A single number is a vector of length 1. Combining it with a longer vector recycles it to match, which is the idiom behind shifting or scaling every element at once.

TipExpert Insight: Vectorisation Replaces Loops

Coming from languages that force you to loop over arrays, it is tempting to write for (i in 1:length(x)) y[i] <- x[i] + 1. R lets you write y <- x + 1. The vectorised form is shorter, clearer, and much faster, because the loop happens in compiled C code instead of interpreted R code. Reach for vectorisation first; reach for a for loop only when you genuinely cannot avoid it.


11.2 Vector Summary Functions

NoteCollapsing a Vector to a Single Value
Function What It Returns
sum(x) Sum of all elements.
prod(x) Product of all elements.
mean(x) Arithmetic mean.
median(x) Middle value (50th percentile).
min(x), max(x) Smallest, largest element.
range(x) Vector of c(min, max).
var(x), sd(x) Sample variance and standard deviation.
cumsum(x), cumprod(x) Running sum and product (returns a vector the same length as x).
cummax(x), cummin(x) Running maximum and minimum.

Summary

Concept Description
Arithmetic and Vectorisation
Element-wise Arithmetic Arithmetic operators apply position by position across vectors
Scalar-Vector Recycling Scalars are recycled to match the vector's length
Vectorisation Replaces Loops Vectorised expressions are faster and clearer than loops
Summary and Logical Helpers
Vector Summary Functions sum, mean, min, max, var, sd collapse a vector to one number
any() and all() any() returns TRUE if any element is true; all() requires all
which() Returns the indices where a logical vector is TRUE
%in% Membership Tests element-wise membership in a set of values
ifelse() Vectorised conditional that returns one of two values per element

11.3 Relational Operations on Vectors

NoteComparison Produces a Logical Vector

Every relational operator returns a logical vector of the same length as its input. That vector then drives filtering, counting, and decision-making downstream.

Noteany(), all(), which(): The Logical-Vector Helpers
Function What It Answers
any(cond) Is at least one element TRUE?
all(cond) Are all elements TRUE?
which(cond) What are the positions of the TRUE elements?
sum(cond) How many elements are TRUE? (logical coerced to integer)
mean(cond) What fraction of elements are TRUE?
TipExpert Insight: sum(cond) Is the Fastest Counter

A vectorised comparison returns TRUEs and FALSEs. When you pass those to sum(), R coerces them to 1s and 0s and adds them up. That is a faster, clearer way to count matches than writing a loop or using length(which(cond)).


11.4 Logical Operations on Vectors

NoteCombining Conditions Element by Element

&, |, !, and xor() all work element-wise on logical vectors and are the right choice when filtering a whole vector. && and || expect single values and are for single-branch if tests.

WarningCommon Mistake: Mixing && and &

&& and || stop at the first element and return a single value. Using them to filter a vector is a bug, not a shortcut. From R 4.3 onward, R warns loudly when you do it; before that, it silently gave the wrong answer.


11.5 Filtering with Logical Vectors

NoteCore Idiom: x[condition]

The result of a relational or logical operation is a logical vector that can be used as an index. Keeping the elements where the condition is TRUE is the most common operation in all of data analysis.

NoteMissing Values Inside Conditions

As Chapter 10 showed, NA leaks through comparisons. When filtering a vector that might contain missing values, guard the condition with !is.na(x) & ....


11.6 ifelse(): A Vectorised if

NoteElement-Wise Branching

ifelse(condition, yes, no) takes three vectors of compatible length (recycling as needed) and returns, for each element, either the “yes” value or the “no” value depending on the condition. It is how you build derived columns without writing a loop.

TipBest Practice: ifelse() Is for Vectors, if Is for Control Flow

if (...) ... else ... returns a single value and decides which code to execute. ifelse(...) returns a vector and is purely a computation. Mixing them up leads to either silent bugs (using if where a vector was expected) or wasted work (using ifelse() inside a function that only needs one result).


11.7 A Worked Example: Two Standard Patterns

NotePattern 1, Centre and Scale

Centring (subtracting the mean) and scaling (dividing by the standard deviation) are so common that R ships a function called scale(). Writing the calculation longhand is a good way to see vector arithmetic at work.

NotePattern 2, Filter, Then Summarise

Answering a real question is usually a two-step move: filter the vector, then summarise what is left.


11.8 Summary

Summary of concepts introduced in this chapter
Concept Description
Arithmetic
Arithmetic Vectorises +, -, *, /, ^, %%, %/% act element by element and recycle a shorter operand
Summary Functions sum, mean, median, min, max, range, sd, var, cumsum, cummax cover most first-pass needs
na.rm = TRUE The canonical way to skip missing values inside a summary function
Relational
Relational Operators Produce logical vectors ready for filtering or counting
any, all, which, sum, mean Five idioms for 'at least one', 'all', 'which positions', 'how many', 'what fraction'
Logical and Filtering
Element-wise Logical & and | apply to every element of a vector
Scalar Logical && and || expect a single value and short-circuit; use only inside if()
Filtering x[cond] is the bedrock idiom; guard with !is.na(x) & ... when gaps exist
ifelse() Vectorised branching for derived columns and label construction
Standard Patterns
Centre and Scale (x - mean(x)) / sd(x) standardises a vector to mean 0 and sd 1
Filter, then Summarise Filter the vector with a logical condition, then call a summary function on what remains
TipApplying This in Practice

Operators turn vectors from “collections of numbers” into “one-line answers”. Whenever you find yourself writing a loop to add, compare, or count, pause and ask whether a vectorised expression would do the same job. In the next chapter you will meet lists, the structure R uses whenever a vector is not enough because the elements do not share a single type.