Section 1 of 8

1 Before you start

Before you begin, take a few minutes to check what you already know and how confident you feel. You will see the same questions again at the end of the module — this helps both you and us measure what you have learned. Click an option for every question and confidence rating, then click Next to continue.

Pre-test

A patient skipped their HbA1c blood test. How should that missing result be stored in R?

Pre-test

A glucose reading of 0 and a missing glucose reading are treated the same way in R. Which statement is correct?

Pre-test

A column has the values 7.2, NA, 6.5. What does mean() of that column return by default?

Pre-test

Why does mean(c(7.2, NA, 6.5, 9.4)) return NA rather than a number?

Pre-test

You run mean(hba1c) on a column with one missing value and it returns NA. Which line gives you the average of the patients who do have a result?

Pre-test

You report mean HbA1c using na.rm = TRUE. What else must you report for the summary to be honest?

Pre-test

Which line correctly tests which elements of the vector x are missing?

Pre-test

You want the number of missing HbA1c values in the vector hba1c. Which line gives it directly?

Pre-confidence

I can recognise NA as R's missing-value marker and explain why it is not the same as 0 or an empty string "".

Not at all confident
Fully confident
Pre-confidence

I can explain why one NA makes mean() and sum() return NA, and fix it with na.rm = TRUE while reporting how many values were missing.

Not at all confident
Fully confident
Pre-confidence

I can find and count missing values with is.na() and sum(is.na(x)), and count complete records with complete.cases().

Not at all confident
Fully confident
Section 2 of 8

2 Introduction

In Part I you stored values in objects, built vectors with c(), named the four core data types, and read a clinical .csv into a tibble. Now you meet the thing that makes real clinical data harder than textbook data: some values are simply not there.

Missing values are normal in clinical work — a patient skips a blood test, a form is left blank, a result is lost. R has one precise marker for these gaps, and handling it well is the difference between an honest summary and a misleading one. This part covers:

  • NA — R's marker for a missing value, which is not 0 and not an empty string "".
  • Silent propagation — how a single NA makes mean() and sum() return NA, the classic beginner shock.
  • na.rm = TRUE — the one argument that drops missing values before a summary runs, and why you must always report how many you dropped.
  • Finding and counting gaps — asking which values are missing with is.na(), counting them with sum(is.na(x)), and checking whole rows with complete.cases().
A missing value (NA) is genuine unknown information, so R spreads it through any calculation rather than guessing, which is why you must deliberately find the gaps and count them before you summarise.
A missing value (NA) is genuine unknown information, so R spreads it through any calculation rather than guessing, which is why you must deliberately find the gaps and count them before you summarise.

By the end of this part you will be able to recognise NA in your data and distinguish it from 0 and "", explain why mean() returns NA when a value is missing and fix it with na.rm = TRUE while reporting the count, and find and count missing values with is.na(), sum(is.na(x)), and complete.cases().

Try every snippet in the R Scratchpad on the right — the dataset diabetes_clinic.csv is already loaded, and two of its HbA1c values are missing on purpose so you can see exactly what NA does.

Section 3 of 8

3 What NA means, and what it does not

When a value is missing, R stores a special marker called NA, which stands for not available. It is R's way of saying "there should be a value here, but we do not have it." You write it as the bare word NA, with no quotes.

Picture a column of HbA1c results where one patient missed their blood test. That cell is not zero, and it is not blank text — it is NA. The vector c(7.2, NA, 6.5, 9.4) holds four patients, one of whom has no result yet.

A missing value in R is stored as the distinct marker NA, a placeholder for a value that should exist but is not available, and it is neither the number 0 nor an empty string.
A missing value in R is stored as the distinct marker NA, a placeholder for a value that should exist but is not available, and it is neither the number 0 nor an empty string.

This is the trap to name out loud: NA is not the same as 0, and not the same as an empty string "". A glucose of 0 is a real (alarming) measurement. An empty string "" is a piece of text that happens to be empty. NA means we genuinely do not know the value. Treating a missing result as 0 would drag every average down and invent patients who were never measured.

Every data type can carry NA: a missing number, a missing label, a missing TRUE/FALSE. The marker is the same word NA in each case, so you only have to learn it once.

Try it out

Try this snippet in the R Scratchpad on the right.

Try this snippet
hba1c <- c(7.2, NA, 6.5, 9.4)
hba1c
A missing value (NA) is not the number 0 and not an empty string "", it means the value is genuinely unknown, so treating it as 0 invents data that was never measured and distorts every summary you compute.
A missing value (NA) is not the number 0 and not an empty string "", it means the value is genuinely unknown, so treating it as 0 invents data that was never measured and distorts every summary you compute.
Section 4 of 8

4 Why one NA breaks your whole calculation

Now the shock that catches almost every beginner. Arithmetic on a missing value gives a missing answer. This is called NA propagation: once an NA enters a calculation, it spreads to the result, because R cannot know what the answer would have been.

Think it through. What is NA + 1? We do not know the first number, so we cannot know the total — R answers NA. The same logic applies to a whole vector: if any patient's HbA1c is missing, the average of the column is unknown too.

So mean(c(7.2, NA, 6.5, 9.4)) returns NA, not a number. R is not broken and it is not skipping the gap for you — it is refusing to make up a value. The classic mistake is to expect mean() and sum() to ignore missing values automatically. They do not. By default, one NA makes the whole result NA.

A single NA anywhere in a calculation forces the entire result to NA, because R refuses to guess the missing value rather than silently skipping it.
A single NA anywhere in a calculation forces the entire result to NA, because R refuses to guess the missing value rather than silently skipping it.
Try it out

Try this snippet in the R Scratchpad on the right.

Try this snippet
hba1c <- c(7.2, NA, 6.5, 9.4)
mean(hba1c)
sum(hba1c)
Section 5 of 8

5 The fix: na.rm = TRUE, and always report how many

The fix is one argument. Adding na.rm = TRUE tells the function to remove the NA values before it computes. The name reads literally: na (the missing values), rm (remove), set to TRUE.

So mean(hba1c, na.rm = TRUE) averages only the patients who actually have a result. The same argument works in sum(), median(), sd(), and most summary functions.

But na.rm = TRUE is not a magic eraser — it is a decision, and a decision you must report. Always say how many values were missing alongside the average. "Mean HbA1c was 7.8% (n = 142; 8 missing)" is honest science. "Mean HbA1c was 7.8%" hides that you quietly dropped eight patients. The number you drop is part of your result.

Try it out

Try this snippet in the R Scratchpad on the right.

Try this snippet
hba1c <- c(7.2, NA, 6.5, 9.4)
mean(hba1c, na.rm = TRUE)
sd(hba1c, na.rm = TRUE)
na.rm = TRUE removes missing values before a calculation runs, but because dropping data changes the result, the count of what you dropped must be reported alongside the number.
na.rm = TRUE removes missing values before a calculation runs, but because dropping data changes the result, the count of what you dropped must be reported alongside the number.
Section 6 of 8

6 Finding and counting missing data

Before you can report how many values are missing, you have to count them — and before you count, you have to ask R which values are missing. That question has one correct tool.

Use is.na(x), which tests each element and returns a logical vector: TRUE where the value is missing, FALSE where it is present. For is.na(c(7.2, NA, 6.5, NA)) you get back FALSE TRUE FALSE TRUE.

Try it out

Try this snippet in the R Scratchpad on the right.

Try this snippet
hba1c <- c(7.2, NA, 6.5, NA)
is.na(hba1c)
sum(is.na(hba1c))
is.na() turns each value into TRUE when it is missing and FALSE when it is present, and because TRUE counts as 1, summing that logical vector gives you the exact number of missing values.
is.na() turns each value into TRUE when it is missing and FALSE when it is present, and because TRUE counts as 1, summing that logical vector gives you the exact number of missing values.

To count the gaps, wrap is.na() in sum(). R treats TRUE as 1 and FALSE as 0, so sum(is.na(x)) adds up the TRUEs and gives you the number of missing values directly. This is the line you will type constantly.

To find missing values you must use is.na(), because == compares against an unknown and returns NA for every element, never TRUE; wrapping is.na() in sum() then counts the gaps since TRUE counts as 1 and FALSE as 0.
To find missing values you must use is.na(), because == compares against an unknown and returns NA for every element, never TRUE; wrapping is.na() in sum() then counts the gaps since TRUE counts as 1 and FALSE as 0.

When you have many columns, complete.cases(data) checks each row and returns TRUE only for rows with no missing value anywhere. sum(complete.cases(clinic)) tells you how many patients have a complete record, and sum(!complete.cases(clinic)) how many have at least one gap.

Try it out

Try this snippet in the R Scratchpad on the right.

Try this snippet
library(readr)
clinic <- read_csv("diabetes_clinic.csv")
sum(complete.cases(clinic))
sum(!complete.cases(clinic))
Try it out

Try this snippet in the R Scratchpad on the right.

Try this snippet
library(readr)
clinic <- read_csv("diabetes_clinic.csv")
sum(is.na(clinic$hba1c))
nrow(clinic)
complete.cases() inspects every column in a row at once and marks the row complete only when nothing is missing, so summing it counts fully recorded patients while summing its negation counts patients with at least one gap.
complete.cases() inspects every column in a row at once and marks the row complete only when nothing is missing, so summing it counts fully recorded patients while summing its negation counts patients with at least one gap.
Section 7 of 8

7 Check your understanding

You have reached the end of the module. Try the same questions again — your answers here, paired with your pre-test answers, are how we measure what the module taught you. Answer every question and confidence rating, then click Submit and see results to view your score.

Post-test

A patient skipped their HbA1c blood test. How should that missing result be stored in R?

Post-test

A glucose reading of 0 and a missing glucose reading are treated the same way in R. Which statement is correct?

Post-test

A column has the values 7.2, NA, 6.5. What does mean() of that column return by default?

Post-test

Why does mean(c(7.2, NA, 6.5, 9.4)) return NA rather than a number?

Post-test

You run mean(hba1c) on a column with one missing value and it returns NA. Which line gives you the average of the patients who do have a result?

Post-test

You report mean HbA1c using na.rm = TRUE. What else must you report for the summary to be honest?

Post-test

Which line correctly tests which elements of the vector x are missing?

Post-test

You want the number of missing HbA1c values in the vector hba1c. Which line gives it directly?

Post-confidence

I can recognise NA as R's missing-value marker and explain why it is not the same as 0 or an empty string "".

Not at all confident
Fully confident
Post-confidence

I can explain why one NA makes mean() and sum() return NA, and fix it with na.rm = TRUE while reporting how many values were missing.

Not at all confident
Fully confident
Post-confidence

I can find and count missing values with is.na() and sum(is.na(x)), and count complete records with complete.cases().

Not at all confident
Fully confident
Section 8 of 8

8 Your results

Here is how your post-test answers compare with your pre-test answers. The pre/post pairing is the most reliable way to see what this module actually taught you.

Your score

Submit the post-test to see your results.

Muddiest point

What is the one thing from this module that is still unclear to you?

Rate this module

Overall, how would you rate this module?

How likely are you to recommend this module to a peer? (0 = not at all, 10 = extremely likely)