Introduction to R
  • Schedule
  • Syllabus
  • Instructors
  • Practice
  • Project
  • Resources

On this page

  • Questions
    • Question 1: Missingness statistics
    • Question 2: Visualizing missingness
    • Question 3: Filtering out missing values
    • Question 4: Filling in and recoding missing values

Practice 9 Answers

PUBH 523/623

Author

Nicky Wakim

Modified

August 24, 2026

Questions

Question 1: Missingness statistics

In Lesson 34, we saw how to get quick, high-level counts of missingness before doing anything else with a dataset.

Part A: Import dataset

WarningTask

Import hrs_data.rds and assign it to hrs_data.

Answer:

Here’s an example to show you the pattern — the rest of your answers should follow this same style, with code chunks and a #| ... chunk options as needed.

hrs_data <- import(here("data", "hrs_data.rds"))
Warning: Missing `trust` will be set to FALSE by default for RDS in 2.0.0.
glimpse(hrs_data)
Rows: 2,728
Columns: 32
$ HHID          <fct> 552754, 555339, 559734, 558143, 554789, 551018, 550516, …
$ pn            <fct> 010, 020, 010, 020, 020, 010, 010, 010, 020, 010, 010, 0…
$ id            <chr> "552754_010", "555339_020", "559734_010", "558143_020", …
$ BIRTHYR       <dbl> 1967, 1966, 1967, 1973, 1950, 1970, 1960, 1971, 1952, 19…
$ BIRTHMO       <dbl> 9, 7, 3, 10, 7, 1, 1, 4, 9, 1, 10, 6, 12, 5, 2, 7, 11, 1…
$ BIRTHDATE     <dbl> 2814, 2387, 2602, 5036, -3153, 3667, 14, 4122, -2664, 22…
$ proxy         <fct> Respondent, Respondent, Respondent, Respondent, Responde…
$ coupled       <fct> Not a couple HH, Couple HH, Couple HH, Couple HH, Couple…
$ sex           <fct> Female, Female, Male, Male, Male, Female, Male, Male, Ma…
$ age_mo        <dbl> 665, 676, 663, 600, 860, 632, 749, 621, 845, 679, 641, 6…
$ age_yr        <dbl> 55, 56, 55, 50, 71, 52, 62, 51, 70, 56, 53, 54, 53, 60, …
$ ed            <dbl> NA, 12, NA, 16, 16, 17, 11, 13, 16, NA, NA, NA, 12, 12, …
$ degree        <fct> NA, High school diploma, NA, Master's degree, Master's d…
$ race_original <fct> NA, White/Caucasian, NA, White/Caucasian, Black/African …
$ smoke_ever    <fct> No, No, No, No, No, No, No, No, No, No, No, No, No, No, …
$ smoke_now     <fct> No, Yes, Yes, Yes, No, No, Yes, No, No, Yes, No, No, No,…
$ drink         <fct> No, Yes, Yes, Yes, Yes, Yes, Yes, Yes, Yes, Yes, No, No,…
$ height        <dbl> 1.4986, 1.6002, 1.9050, 1.8796, 1.7526, 1.5494, 1.7780, …
$ srh           <fct> Fair, Good, Very Good, Excellent, Good, Poor, Excellent,…
$ act_vig       <fct> Never, Never, Never, >1 per week, 1 per week, Never, Nev…
$ bp            <fct> No, Yes, Yes, No, No, No, Yes, Yes, Yes, No, No, No, Yes…
$ diab          <fct> No, No, No, No, Yes, Yes, No, No, No, No, No, No, No, Ye…
$ cancer        <fct> No, No, No, No, Yes, No, No, No, Yes, No, No, No, No, No…
$ lung          <fct> No, No, No, No, No, Yes, No, No, No, No, No, No, Yes, No…
$ hrt           <fct> No, No, No, No, Yes, No, No, No, No, No, No, No, No, Yes…
$ strk          <fct> No, No, No, No, No, No, No, No, No, No, No, No, No, Yes,…
$ psych         <fct> No, No, No, No, Yes, Yes, No, No, No, Yes, Yes, No, Yes,…
$ sleep         <fct> Yes, Yes, No, No, Yes, Yes, No, Yes, Yes, Yes, Yes, No, …
$ arth          <fct> No, No, Yes, Yes, No, Yes, No, No, No, No, No, No, No, N…
$ cond_count    <dbl> 0, 1, 2, 1, 4, 4, 1, 1, 2, 1, 1, 0, 2, 4, 0, 1, 0, 0, 1,…
$ cesd          <dbl> 1, 6, 0, 1, 3, 7, 1, 2, 1, 1, 7, 2, 4, 0, 0, 4, 2, 1, 1,…
$ income        <dbl> 35000.00, 0.00, 108400.00, 526192.00, 148320.00, 28266.5…

Part B: Total missingness in the dataset

WarningTask

Use n_miss() on hrs_data to find the total number of missing cells across the entire dataset.

Answer:

Not given

Part C: Proportion of missingness in the dataset

WarningTask

Use prop_miss() on hrs_data to find the proportion of all cells in the dataset that are missing.

Answer:

prop_miss(hrs_data)
[1] 0.01774423

Part D: Missingness in a single variable

WarningTask

Use n_miss() on just the srh column of hrs_data to find how many values are missing for self-reported health.

TipTip

You can pull out a single column with $, e.g. hrs_data$srh.

Answer:

Not given

Part E: Missingness summary table

WarningTask

Use miss_var_summary() on hrs_data to get one row per variable with its count and percent missing. Pipe the result into gt() to make it a nicer table. Assign the table to hrs_01_summary_table and display it.

Which variable has the most missing values? Which has the least?

Answer:

Not given

Question 2: Visualizing missingness

In Lesson 34, we also covered a few plotting functions from naniar that help us see where and how data is missing.

Part A: A bird’s-eye view with vis_miss()

WarningTask

Use vis_miss() on hrs_data to visualize missingness across the whole dataset. Assign the plot to hrs_02_plot_vis_miss and display it.

Answer:

Start with something like:

hrs_02_plot_vis_miss <- hrs_data |> 
  ______()

Part B: Customize the plot

WarningTask

vis_miss() returns a ggplot2 object, so you can add layers to it just like the plots you built last week. Take hrs_02_plot_vis_miss and add a theme() layer that rotates the x-axis text labels by 20 degrees (angle = 20) so they’re easier to read. Assign the result to hrs_03_plot_vis_miss_clean and display it.

Looking at your plot, is missingness concentrated in a few variables, spread across many observations, or both?

TipTip

theme(axis.text.x = element_text(angle = 20, hjust = 0)) adjusts the angle and alignment of the x-axis text.

Answer:

Not given

Part C: Missingness by group with gg_miss_fct()

WarningTask

Use gg_miss_fct() on hrs_data, stratifying by degree (set fct = degree). Assign the plot to hrs_04_plot_fct and display it.

Does missingness look similar across degree, or does one group have noticeably more missing data for certain variables?

Answer:

Not given

Part D: Missingness intersections with gg_miss_upset()

WarningTask

Use gg_miss_upset() on hrs_data, setting nsets = 10 and text.scale = 2. Assign the plot to hrs_05_plot_upset and display it.

Which combination of variables is missing together most often? Is there a variable that tends to be missing mostly on its own?

Answer:

Not given

Question 3: Filtering out missing values

In Lesson 35, we covered how to remove rows with missing data, and a common gotcha when using filter() on a column with NAs.

Part A: Check missingness before you drop anything

WarningTask

Before dropping any rows, pipe hrs_data into miss_var_summary() and then gt() again, just like Question 1 Part E, so you have a “before” table to compare to later. Assign it to hrs_06_summary_before.

TipTip

Getting in the habit of checking missingness before and after you drop or fill in values helps you catch mistakes, like accidentally dropping more rows than you meant to.

Answer:

Not given

Part B: Drop rows missing anything

WarningTask

Use drop_na() on hrs_data (with no arguments) to remove any row that has any missing value. Assign the result to hrs_07_dropna_all. How many rows are left compared to hrs_data?

Answer:

Not given

Part C: Drop rows missing specific columns

WarningTask

Now use drop_na() on hrs_data again, but this time only target the act_vig and bp columns, so rows are only dropped if one of those two columns is missing. Assign the result to hrs_08_dropna_cols. How does the number of rows compare to hrs_07_dropna_all?

Answer:

Start with something like:

hrs_08_dropna_cols <- hrs_data |> 
  drop_na(______, ______)

Part D: The filter() gotcha

WarningTask

Use tabyl() (from janitor) on hrs_data$smoke_ever to see how many missing values smoke_ever has and confirm its categories.

Then, use filter() on hrs_data to keep only rows where smoke_ever != "No". Assign the result to hrs_09_filter_gotcha. Use tabyl() on the smoke_ever column of hrs_09_filter_gotcha to check what happened to the missing values.

Answer:

Not given

Part E: Fixing the gotcha with filter_out()

WarningTask

Redo Part D, but use filter_out() instead of filter() to exclude smoke_ever == "No" while keeping the NAs. Assign the result to hrs_10_filter_out_fixed. Confirm with tabyl() that the missing values are still there this time.

Answer:

Not given

Question 4: Filling in and recoding missing values

In Lesson 35, we also covered how to fill in missing values instead of dropping them, and how to turn a specific value into NA when needed.

Part A: Fill in a numeric variable

WarningTask

Use mutate() and replace_na() to create a new column, cesd_filled, in hrs_data that replaces any missing cesd (depression symptom score) with the mean cesd (use na.rm = TRUE inside mean()). Assign the result to hrs_11_cesd_filled.

Is filling in a missing value with the overall mean always a good idea? Why or why not?

Answer:

Start with something like:

hrs_11_cesd_filled <- hrs_data |> 
  mutate(
    cesd_filled = replace_na(______, ______)
  )

Part B: Fill in a factor variable

WarningTask

Starting from hrs_11_cesd_filled, use mutate() and fct_na_value_to_level() to create a new column, degree_filled, that replaces any missing degree with a new level called "Unknown". Assign the result to hrs_12_degree_filled.

TipTip

replace_na() does not work on factor columns. Use fct_na_value_to_level() instead whenever the column you’re filling in is a factor.

Answer:

Start with something like:

hrs_12_degree_filled <- hrs_11_cesd_filled |> 
  mutate(
    degree_filled = fct_na_value_to_level(______, level = ______)
  )

Part C: Check your work

WarningTask

Use tabyl() to compare degree and degree_filled in hrs_12_degree_filled (hint: tabyl() can take two column names to cross-tabulate them). Confirm that every row that was NA in degree now shows "Unknown" in degree_filled.

Answer:

Not given

Part D: Turn a valid value into NA

WarningTask

Suppose, just for practice, we wanted to treat everyone who answered "No" to drink (whether they drink alcohol) as missing instead (this is not something you’d actually want to do with real data, but it shows how the function works). First, use mutate() to make a copy of the column called drink_na. Then use replace_with_na() to replace "No" with NA in the drink_na column. Assign the result to hrs_13_replace_with_na.

Use tabyl() to compare the original drink column to your new drink_na column and confirm the replacement worked as expected.

Answer:

Start with something like:

hrs_13_replace_with_na <- hrs_12_degree_filled |> 
  mutate(drink_na = drink) |> 
  replace_with_na(replace = list(
    ______ = ______
  ))

Part E: Summarize your final, cleaned dataset

WarningTask

Take hrs_12_degree_filled (from Part B, before Part D’s practice-only recoding) and run miss_var_summary() and gt() on it one more time. Assign it to hrs_14_summary_after. Compare it to hrs_06_summary_before from Question 3 Part A. Which variables now show less missingness, and why?

Answer:

Not given