Missing and Special Values
Back at the checkout with the same six-item basket from Lesson 3 (milk, bread, eggs, apples, coffee, rice) something has gone wrong: the eggs barcode was smudged and never scanned, so its price came through blank. In R that blank is not zero and not an empty word. It is a special marker, NA, that means "a value belongs here, but we do not know what it is." This lesson is about that marker and its three cousins, and the surprising amount of damage a single hole can do if you do not handle it.
By the end of this lesson you will be able to:
- Explain what
NAmeans and predict how a single one spreads through asum()or amean() - Skip the holes with
na.rm = TRUE, and find them withis.na()(and dodge theNA == NAtrap) - Tell
NA,NaN,Inf, andNULLapart: what each one means and how you get it
Prerequisites: Lesson 1 (running R, <-, calling a function), Lesson 2 (building a vector with c(), the four types), and Lesson 3 (operators run element by element, and sum()/mean() of a condition count and average). The table below is the basket as a data frame, with that missing egg price already in it. Toggle the columns, then press Build it in R.
NA marks a value that is missing
Real data has holes: a survey question left blank, a sensor that dropped out, a barcode that did not scan. R has one marker for every such hole, NA (short for "Not Available"). It is not a typo for zero and not an empty string ""; it is R's honest way of saying we do not know this value.
Each lesson starts a fresh R session, so let us rebuild the basket right here, with the eggs price recorded as the hole it really is:
Notice two things. First, the hole prints as NA, sitting in the third slot exactly where the egg price would go. Second, the hole still takes up a slot: the basket is six items long, not five. That matters later when we meet NULL, which behaves the opposite way.
One NA spreads to your whole answer
Here is the rule that catches every beginner. **Any calculation that touches an NA returns NA.** R reasons honestly: if you do not know the eggs price, then you also do not know the basket's total, and you do not know the average price. An unknown ingredient makes an unknown result.
So the very sum() and mean() you learned in Lesson 3 now come back blank, because one of the six prices is unknown:
The multiplication is the clearest picture of it: five of the six line totals compute fine, but the eggs slot is NA, and the moment you sum() that vector the single hole swallows the whole result. The flow below traces how one missing price turns the entire total into NA.
What does the average come back as?
Your price vector has six values, and one of them (eggs) is NA. The other five are perfectly good numbers. With no extra arguments, what does mean(price) return?
Tell R to skip the holes: na.rm = TRUE
Most summary functions take an argument that says "ignore the missing values and work with what is left." It is called na.rm (NA-remove), and it is FALSE by default, which is why sum() and mean() blanked out above. Set it to TRUE and R computes over only the known values:
The five known prices (2.50, 1.80, 4.00, 7.50, 5.20) add to 21.0, and their average is 21 / 5 = 4.2. The same na.rm = TRUE works on min(), max(), median(), sd(), and friends. If instead you want to throw away the incomplete entries entirely, na.omit(price) returns the five-value vector with the hole removed.
na.rm = TRUE is useful but it is not free: it quietly changes the question. mean(price, na.rm = TRUE) is no longer "the average price of my six-item basket"; it is "the average price of the five items I happened to scan." Usually that is what you want, but always know that you are answering about the data you have, not the data you wish you had.Total the basket you can price
You want the basket total for the items whose price you actually know. price * quantity gives each line total (with NA at the eggs slot), and sum() adds them. Add the one argument that tells sum() to skip the hole. You are expecting 36.7.
Show answer
known_total <- sum(price * quantity, na.rm = TRUE)
known_total
#> [1] 36.7is.na() asks "which values are missing?"
Before you can skip or fix holes, you have to find them. Your instinct from Lesson 3 might be to compare against NA with ==. That instinct fails, and failing to know why is one of the most common R bugs:
Every answer is NA, even for the prices that are plainly there. Why? Because == asks "is this value equal to that value?", and NA means "unknown." Asking "is 2.50 equal to an unknown number?" has only one honest answer: unknown. So anything == NA is NA, never TRUE. You can never test for missingness with ==.
The right tool is the dedicated function is.na(), which returns a clean TRUE/FALSE for every element, with TRUE exactly at the holes:
NA == NA is NA, not TRUE: two unknowns are not "equal," because you do not know either one. Whenever you mean "is this value missing?" reach for is.na(x), never x == NA. sum(is.na(x)) then counts the missing values, and mean(is.na(x)) gives the fraction missing.How do you flag the missing prices?
You want a TRUE/FALSE for every item, TRUE where its price is missing and FALSE where it is known. Which expression gives you that?
NaN, Inf, and NULL: the rest of the family
NA is the one you will meet daily, but R has three more special values worth recognizing on sight. The first two appear when the arithmetic itself breaks down:
NaN ("Not a Number") is what you get from a calculation with no defined answer, like 0/0 or the square root of a negative number. Inf and -Inf are positive and negative infinity, from dividing a non-zero number by zero or from a result too large to represent. Each has its own test, and watch this one closely: NaN also counts as missing (every NaN is an NA, but not the reverse):
The last cousin is different in kind. NULL is not a hole in a vector; it is the absence of anything at all, an object of length zero. Where NA keeps its slot, NULL simply vanishes when you build a vector:
Here is the whole family on one card:
| value | what it means | a typical way you get it | test it with |
|---|---|---|---|
NA |
a value exists but is unknown (missing) | a reading that did not scan | is.na() |
NaN |
an undefined numeric result | 0 / 0, sqrt(-1) |
is.nan() |
Inf / -Inf |
infinity: a number too big to represent | 1 / 0, overflow |
is.infinite() |
NULL |
nothing at all, not even a slot | a function that returns nothing | is.null() |
Which special value appears?
A sales report computes each category's average price as total / count. One brand-new category had no sales yet, so both total and count are 0, and R evaluates 0 / 0 for it. What shows up in that cell?
References
A few trustworthy places to take this further, all free:
- R for Data Science (2e): Missing values - a practical chapter on explicit vs implicit missing values and how to handle them in real data.
- An Introduction to R: Missing values - the R project's own first treatment of
NAandis.na(), straight from the source. - The R Language Definition: special values - the authoritative spec for how
NA,NaN,Inf, andNULLare defined and behave. - Advanced R (2e): Vectors - the precise distinction between
NULL(length zero) andNA(a missing element), with the full type rules.
Lesson 4 complete
You can now handle the holes that real data always has. You saw that NA marks a value that exists but is unknown, and that a single one spreads through sum(), mean(), and elementwise math because R refuses to guess past it; that na.rm = TRUE (and na.omit()) let you compute over the known values, as long as you remember it answers a slightly different question; that is.na() is the only safe way to find missing values, since NA == NA is itself NA; and that NaN (undefined math), Inf (infinity), and NULL (nothing at all) are three more special values, each with its own meaning and its own test.
That completes R Foundations: The Basics. You have gone from your first <- to vectors, types, operators, recycling, and now the special values that trip up everyone who skips them. Next, the Foundations track moves from single vectors to the structures you will use every day, data frames and lists, and the control flow (if, for) that ties your code together. Head back to the course page to keep going.