Operators, Recycling, and Coercion
You are at the checkout with six things in your basket: milk, bread, eggs, apples, coffee, rice. Their unit prices are 2.50, 1.80, 3.20, 4.00, 7.50, 5.20 dollars, and you bought 2, 1, 1, 3, 1, 2 of them. In Lesson 2 you learned to hold each of those rows in a vector. This lesson is about doing things with them: working out every line total in one stroke, asking "which items cost more than \$5?", and applying a discount to the whole basket at once.
By the end of this lesson you will be able to:
- Use R's operators (
+ - * /,>,&) on a whole vector at once, element by element - Build TRUE/FALSE vectors to answer questions about your data, and count how many pass
- Predict how R recycles a shorter vector to fit a longer one, and what its warning means
Prerequisites: Lesson 1 (running R, assigning with <-, calling a function) and Lesson 2 (building a vector with c(), the four types, and the coercion ladder). The box below holds three of your prices; press Run any time, and try the + "hi" (text) button to see coercion at work before we even begin.
Operators work on the whole vector at once
Here is the move that makes R feel different from a calculator. In most languages, to multiply each price by its quantity you would write a loop. In R you just write the multiplication once, and R applies it position by position across the whole vector. First, build the basket (each lesson starts a fresh R session, so we create the data right here; run this once):
Now the line total for every item, in one expression. R lines up price[1] with quantity[1], price[2] with quantity[2], and so on, multiplying each pair:
That is what people mean when they call R vectorized: an operator on a vector returns a vector, computed one matching pair at a time. The same goes for +, -, /, ^ (powers), and the two whole-number helpers %% (remainder) and %/% (integer division); every one of them runs element by element. Here is the pairing price * quantity made visible:
Total each line yourself
The basket is already built (price and quantity, six values each). Replace the blank with the expression that multiplies them element by element, so line_total holds each item's cost. You are expecting 5.0 1.8 3.2 12.0 7.5 10.4.
Show answer
line_total <- price * quantity
line_total
#> [1] 5.0 1.8 3.2 12.0 7.5 10.4Comparing builds a TRUE/FALSE vector
Operators are not only for arithmetic. A comparison like > asks a yes/no question of every element and hands back a logical vector, one TRUE or FALSE per item. These are the same logicals you met in Lesson 2, now produced by a question:
The full set of comparisons is >, <, >=, <=, == (equal to, two equals signs), and != (not equal). You combine two logical vectors with the logical operators: & (and, TRUE only where both are TRUE), | (or, TRUE where either is), and ! (not, which flips each value). A logical vector can also pick out the elements of another vector where it is TRUE, which is how you turn a question into an answer:
&, |, and ! work element by element, lining up the TRUE/FALSE vectors position by position, exactly as * lined up the numbers. Their doubled cousins && and || look at only the FIRST element and return a single TRUE/FALSE, so they are for one yes/no answer (inside an if), never for a whole vector. Reaching for && when you meant & is the most common beginner slip here.Which operator answers the question?
You want a TRUE/FALSE for every item that is both over \$5 and bought more than once. Which expression gives you that, one value per item?
& combines the two logical vectors element by element, returning TRUE only where both conditions hold, one result per item.| is OR: it is TRUE when EITHER condition holds, not both. You asked for AND, which is &.TRUE counts as 1
Here is where Lesson 2's coercion ladder pays off. The moment a logical value meets arithmetic, R coerces it up the ladder logical < integer < double: TRUE becomes 1 and FALSE becomes 0. That single rule makes counting and averaging fall out for free:
sum() of a condition counts how many are TRUE, because it is adding 1s and 0s. mean() of a condition gives the proportion that are TRUE, for the same reason: two of the six prices clear \$5, so the mean is 2/6, about 0.33. You will use sum(condition) and mean(condition) constantly. The interactive below is the same ladder from Lesson 2; add a logical and a number and watch them land on one shared type:
What does the count return?
R has no separate "count" happening here; it is plain arithmetic on TRUE/FALSE. For our basket, where two items cost more than \$5, what does sum(price > 5) return?
price > 5 is FALSE FALSE FALSE FALSE TRUE TRUE; coerced to 0 0 0 0 1 1, those sum to 2.sum() does arithmetic, not a yes/no. The logicals become 1s and 0s and add up to a count (2), not a single TRUE.Recycling: R stretches the shorter vector
So far every operation paired two vectors of the same length. But you have already used a mismatch without noticing: price * 0.9 multiplies six prices by one number. R handles this by recycling: when one vector is shorter, R repeats it from the start, as many times as needed, to match the longer one. A single value is just the simplest case, repeated six times:
Recycling works for any length that divides evenly. Say the shop runs an "every second item half price" promo, a length-2 pattern c(1, 0.5). R recycles it as 1, 0.5, 1, 0.5, 1, 0.5 to cover all six items:
The repeating rate column is exactly what recycling does, made visible:
| item | price | rate (recycled) | charged |
|---|---|---|---|
| milk | 2.50 | 1.0 | 2.50 |
| bread | 1.80 | 0.5 | 0.90 |
| eggs | 3.20 | 1.0 | 3.20 |
| apples | 4.00 | 0.5 | 2.00 |
| coffee | 7.50 | 1.0 | 7.50 |
| rice | 5.20 | 0.5 | 2.60 |
But if the longer length is not a clean multiple of the shorter one, R recycles as far as it can and warns you that something is probably off. A length-4 pattern against six prices does not divide evenly:
length().Discount the whole basket
The shop offers a flat 10% off everything. A 10% discount means paying 90%, so multiply every price by 0.9, a single value recycled to all six. Fill in the blank, then the second line totals up the discounted basket. The discounted total should come to 35.91 (exactly 90% of the \$39.90 full price).
Show answer
deal <- price * 0.9
sum(deal * quantity)
#> [1] 35.91References
A few trustworthy places to take this further, all free:
- An Introduction to R: numbers and vectors - the canonical first treatment of vector arithmetic and the recycling rule, straight from the R project.
- R for Data Science (2e): Logical vectors - comparisons,
&/|/!, and usingsum()/mean()on a condition to count and average. - Advanced R (2e): Vectors - the precise coercion rules behind "TRUE becomes 1", with the full type hierarchy.
- The R Language Definition: Arithmetic operators - the authoritative spec for how operators, coercion, and recycling are defined.
Lesson 3 complete
You now do things with vectors, not just build them. You saw that R's operators are vectorized, running element by element so price * quantity needs no loop; that comparisons (>, ==) build TRUE/FALSE vectors you combine with &, |, and !; that logicals coerce to 1 and 0 in arithmetic, so sum() and mean() of a condition count and average it; and that recycling quietly stretches a shorter vector to fit a longer one, warning you only when the lengths do not divide evenly.
Next, Lesson 4: Missing and Special Values. Real data has holes, and R marks them with NA (and meets NULL, NaN, and Inf along the way). You will see how a single NA ripples through exactly the operators, sums, and means you just learned, and the handful of tools that keep it from quietly wrecking your results.