Detecting and Counting Missing Values
Use is.na(), anyNA(), and sum(is.na()) to audit missing data.
Detecting and Counting Missing Values is a free R Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
is.na() — The Core Detection Tool
is.na(x) tests each element of x and returns a logical vector of the same length, with TRUE where the value is NA (or NaN). This is the fundamental tool for detecting missing values.
heights <- c(175, NA, 162, 188, NA, 170, NA, 165)
cat('Heights:', heights, '
')
cat('is.na() :', is.na(heights), '
')
cat('!is.na():', !is.na(heights), '
')Counting NAs with sum(is.na())
Because TRUE == 1 and FALSE == 0 in arithmetic, sum(is.na(x)) counts the total number of missing values. mean(is.na(x)) gives the proportion of missing values.
survey <- c(8, NA, 6, NA, 9, 7, NA, 5, NA, 8)
cat('Survey responses:', survey, '
')
cat('Total NAs: ', sum(is.na(survey)), '
')
cat('Total present:', sum(!is.na(survey)), '
')
cat('NA proportion:', mean(is.na(survey)), '
')
cat('NA percentage:', mean(is.na(survey)) * 100, '%
')anyNA() — Quick Presence Check
anyNA(x) returns a single TRUE if any element is NA, otherwise FALSE. It is faster than any(is.na(x)) because it stops at the first NA found.
complete_data <- c(1, 2, 3, 4, 5)
incomplete_data <- c(1, 2, NA, 4, 5)
cat('anyNA(complete) :', anyNA(complete_data), '
') # FALSE
cat('anyNA(incomplete):', anyNA(incomplete_data), '
') # TRUE
# Use in validation
if (anyNA(incomplete_data)) {
cat('Warning: data contains missing values!
')
}which(is.na()) — Find Positions
which(is.na(x)) returns the indices (positions) of missing values in a vector. This is essential for auditing which observations are missing and for targeted imputation.
temperatures <- c(22.1, NA, 19.8, NA, 25.1, NA, 17.3, 20.0)
cat('Temperatures:', temperatures, '
')
missing_pos <- which(is.na(temperatures))
cat('Missing at positions:', missing_pos, '
')
cat('Number missing:', length(missing_pos), '
')
# What values are NOT missing?
present_pos <- which(!is.na(temperatures))
cat('Present at positions:', present_pos, '
')Counting NAs in a Data Frame
For data frames, is.na(df) returns a logical matrix of the same dimensions. Combining with colSums() gives the count of missing values per column — a quick way to audit data quality.
# Simulate a small data frame
name_col <- c('Alice', 'Bob', 'Carol', 'Dave', 'Eve')
age_col <- c(28, NA, 32, NA, 29)
sal_col <- c(55000, 72000, NA, 90000, 61000)
scr_col <- c(88, NA, 75, NA, NA)
cat('Age NAs: ', sum(is.na(age_col)), '
')
cat('Salary NAs:', sum(is.na(sal_col)), '
')
cat('Score NAs: ', sum(is.na(scr_col)), '
')colSums(is.na(df)) Pattern
The pattern colSums(is.na(df)) is the fastest way to count missing values per column in a data frame. It returns a named numeric vector showing how many NAs are in each column.
# Build a data frame manually
df <- data.frame(
name = c('Alice', 'Bob', 'Carol', 'Dave', 'Eve'),
age = c(28, NA, 32, NA, 29),
salary = c(55000, 72000, NA, 90000, 61000),
score = c(88, NA, 75, NA, NA)
)
cat('Missing values per column:
')
print(colSums(is.na(df)))
cat('Total missing:', sum(is.na(df)), '
')Proportion of NAs per Column
Dividing by the number of rows gives the proportion of missing values per column. This is more informative than raw counts when columns have different lengths.
df <- data.frame(
x1 = c(1, NA, 3, NA, 5),
x2 = c(NA, 2, NA, 4, NA),
x3 = c(1, 2, 3, NA, 5)
)
n_rows <- nrow(df)
na_counts <- colSums(is.na(df))
na_pct <- round(na_counts / n_rows * 100, 1)
cat('NA counts: '); print(na_counts)
cat('NA percentage: '); print(na_pct)table(is.na()) — Frequency Count
table(is.na(x)) produces a named frequency table showing how many FALSE (present) and TRUE (missing) values exist. It is easy to read at a glance.
responses <- c(5, NA, 8, 6, NA, 9, 7, NA, NA, 8)
cat('NA frequency table:
')
print(table(is.na(responses)))
# For multiple columns:
cat('Age table:
')
ages <- c(28, NA, 32, NA, 29)
print(table(is.na(ages)))Finding Rows with Any NA
To find which rows in a data frame contain at least one missing value, use apply(is.na(df), 1, any). The result is a logical vector with TRUE for incomplete rows.
df <- data.frame(
id = 1:5,
age = c(28, NA, 32, NA, 29),
salary = c(55000, 72000, NA, 90000, 61000)
)
# Which rows have at least one NA?
has_na <- apply(is.na(df), 1, any)
cat('Rows with any NA:', which(has_na), '
')
cat('Complete rows: ', which(!has_na), '
')
cat('Complete row count:', sum(!has_na), '/', nrow(df), '
')Auditing a Complete Dataset
A practical NA audit workflow: count, locate, and report missing values to understand data quality before analysis. Here is a self-contained audit function.
audit_na <- function(v, label) {
n_total <- length(v)
n_missing <- sum(is.na(v))
pct <- round(n_missing / n_total * 100, 1)
positions <- which(is.na(v))
cat(label, ':', n_missing, '/', n_total,
'(', pct, '%) NA at positions', positions, '
')
}
heights <- c(175, NA, 162, 188, NA, 170)
weights <- c(70, 85, NA, 92, 68, NA)
audit_na(heights, 'Height')
audit_na(weights, 'Weight')NA Detection Summary
Quick reference for NA detection tools:
is.na(x)— logical vector: TRUE where NAanyNA(x)— single TRUE/FALSE: any NA present?sum(is.na(x))— count of NAsmean(is.na(x))— proportion of NAswhich(is.na(x))— positions of NAscolSums(is.na(df))— NA count per columntable(is.na(x))— frequency table of NA/non-NA
x <- c(10, NA, 30, NA, 50, NA, 70)
cat('is.na: ', is.na(x), '
')
cat('anyNA: ', anyNA(x), '
')
cat('sum(is.na): ', sum(is.na(x)), '
')
cat('mean(is.na): ', mean(is.na(x)), '
')
cat('which(is.na):', which(is.na(x)), '
')Quick Check
What does sum(is.na(c(1, NA, 3, NA, 5))) return?
Recap: Detecting and Counting NAs
Well done! Key takeaways from this lesson:
is.na(x)is the primary tool — returns a logical vectorsum(is.na(x))counts missing;mean(is.na(x))gives proportionanyNA(x)is a fast check for presence of any NAwhich(is.na(x))reveals the exact positions of NAscolSums(is.na(df))is the standard way to audit a data frame- Never use
x == NAto detect NA — always useis.na(x)
# Full audit of a data frame in 3 lines
df <- data.frame(a=c(1,NA,3), b=c(NA,2,NA), c=c(1,2,3))
cat('Per column:', colSums(is.na(df)), '
')
cat('Total: ', sum(is.na(df)), '/', nrow(df)*ncol(df), '
')
cat('Complete rows:', sum(complete.cases(df)), '/', nrow(df), '
')Frequently asked questions
Is the “Detecting and Counting Missing Values” lesson free?
Yes — the full text of “Detecting and Counting Missing Values” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.
What will I learn in “Detecting and Counting Missing Values”?
Use is.na(), anyNA(), and sum(is.na()) to audit missing data. You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start R Academy?
No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Detecting and Counting Missing Values” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this R Academy lesson?
Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Understanding NA in R
- Detecting and Counting Missing Values
- Removing and Replacing NA Values
- NA in Calculations and Aggregations