Skip to course content
Free R data course

R Foundations for Data Analysis

Unit 02.03: Five types, and the hole that is not one

Five types will carry you through almost all beginner analysis. Missingness is not a sixth type — it is a hole that takes the shape of whatever surrounds it.

Types, and the one that is not a type

Numbers, text, TRUE/FALSE and dates cover most columns you will meet. R reports two things about each value: its class, which is what it is for, and its typeof, which is how it is stored. They usually agree, and dates are the instructive exception.

NA marks a value that should exist and does not. It is not zero, not an empty string, and not a category. Crucially it is *typed*: an NA in a numeric vector is a numeric NA, so the column keeps working as a column.

The consequence that catches everyone is that NA is unknown, and comparing an unknown to anything gives an unknown.

This block reports the class and storage of one value of each type, then tests NA.


# Five types cover almost all beginner analysis. Missingness is not a sixth.

values <- list(
  visits    = 412L,
  rate      = 0.87,
  ward      = "North",
  staffed   = TRUE,
  recorded  = as.Date("2026-02-14")
)

for (nm in names(values)) {
  cat(sprintf("%-9s class=%-9s typeof=%-9s\n",
              nm, class(values[[nm]])[1], typeof(values[[nm]])))
}

# NA is typed. It takes the type of the vector it sits in.
cat("\nNA inside a numeric vector :", class(c(1, NA)), "\n")
cat("NA inside a character vector:", class(c("a", NA)), "\n")

# And NA is not a value you can test for with ==.
cat("NA == NA gives  :", NA == NA, "\n")
cat("is.na(NA) gives :", is.na(NA), "\n")

visits is integer, rate is numeric stored as a double, ward is character, staffed is logical. recorded has class Date but typeof double, because a date is stored as a count of days from 1970 with a label that tells R how to display it. Then NA == NA gives NA, not TRUE: two unknowns cannot be shown to be equal. is.na(NA) gives TRUE, and that is the only correct way to ask.

The mistake this prevents

The mistake is filtering with x == NA and getting an empty result with no error. The comparison returns NA for every row, no row passes, and it looks like there is no missing data at all.

Takeaway

Check types before you compute, expect dates to be numbers underneath, and always test for missingness with is.na(). Never with ==.