The exercises are part of the Fundamentals of R course. For more, see the R for the Rest of Us website.

Load Packages

Load the tidyverse and janitor packages.

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
## ✔ ggplot2 3.1.1          ✔ purrr   0.3.2     
## ✔ tibble  2.1.1          ✔ dplyr   0.8.0.1   
## ✔ tidyr   0.8.3.9000     ✔ stringr 1.4.0     
## ✔ readr   1.3.1          ✔ forcats 0.4.0
## ── Conflicts ────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
library(janitor)

Import NHANES Data

Import the NHANES data to a data frame called nhanes. Then, use the clean_names function to create clean names for all of your variables.

nhanes <- read_csv("data/nhanes.csv") %>%
  clean_names()
## Parsed with column specification:
## cols(
##   .default = col_character(),
##   ID = col_double(),
##   Age = col_double(),
##   Weight = col_double(),
##   Height = col_double(),
##   BMI = col_double(),
##   DaysPhysHlthBad = col_double(),
##   DaysMentHlthBad = col_double(),
##   SleepHrsNight = col_double(),
##   PhysActiveDays = col_double(),
##   TVHrsDay = col_logical()
## )
## See spec(...) for full column specifications.
## Warning: 4859 parsing failures.
##  row      col           expected    actual              file
## 5001 TVHrsDay 1/0/T/F/TRUE/FALSE 2_hr      'data/nhanes.csv'
## 5002 TVHrsDay 1/0/T/F/TRUE/FALSE More_4_hr 'data/nhanes.csv'
## 5003 TVHrsDay 1/0/T/F/TRUE/FALSE 4_hr      'data/nhanes.csv'
## 5004 TVHrsDay 1/0/T/F/TRUE/FALSE 4_hr      'data/nhanes.csv'
## 5005 TVHrsDay 1/0/T/F/TRUE/FALSE 1_hr      'data/nhanes.csv'
## .... ........ .................. ......... .................
## See problems(...) for more details.

select

With select we can select variables from the larger data frame.

Use select to show just the marital_status variable.

nhanes %>% 
  select(marital_status)

We can also use select for multiple variables.

Use select to show marital_status and education.

nhanes %>%
  select(marital_status, education)

Used within select, the contains function selects variable with certain text in the variable name.

Use the contains function to select variables that ask how many days in the last 30 days the respondent had bad physical and mental health (you should be able to figure out which variables these are from the names).

nhanes %>%
  select(contains("hlth_bad"))

Used within select, the starts_with function selects variable with certain text in the variable name.

Use the starts_with function to select variables that start with the letter h.

nhanes %>% 
  select(starts_with("h"))

We can select a range of columns using the var1:var2 pattern. select all the variables from health_gen to the end.

nhanes %>%
  select(health_gen:smoke_now)

We can drop variables using the -var format. Drop the education variable.

nhanes %>%
  select(-education)

We can drop a set of variables using the -(var1:var2) format. Drop the variables from health_gen to the end.

nhanes %>%
  select(-(health_gen:smoke_now))

mutate

We use mutate we make new variables or change existing ones.

Create a new variable with a specific value

Create a new variable called completed_survey and make all responses to it “Yes”.

nhanes %>%
  mutate(completed_survey = "Yes")

Copy your code from above and then add a line where you select only the completed_survey variable. Don’t forget the pipe (%>%)!

nhanes %>%
  mutate(completed_survey = "Yes") %>% 
  select(completed_survey)

Create a new variable based on other variables

Create a new variable called pct_days_phys_health_bad and calculate it as the percentage of self-reported days of bad physical health in the last 30 days. Remember that the days_phys_hlth_bad variable is a measure of the number of self-reported days of bad physical health in the last 30 days. Then, use select to show the days_phys_hlth_bad and pct_days_phys_health_bad variables.

nhanes %>% 
  mutate(pct_days_phys_health_bad = days_phys_hlth_bad / 30) %>% 
  select(days_phys_hlth_bad, pct_days_phys_health_bad)

Change an existing variable

Round the height variable to a whole number. Then, use select to show only the height variable.

nhanes %>%
  mutate(height = round(height, digits = 0)) %>% 
  select(height)

filter

We use filter to choose a subset of cases.

Use filter to keep only respondents who are divorced. Then, use select to show only the marital_status variable.

nhanes %>% 
  filter(marital_status == "Divorced") %>%
  select(marital_status)

Use filter to keep only respondents who are not divorced. Then, use select to show only the marital_status variable.

nhanes %>% 
  filter(marital_status != "Divorced") %>%
  select(marital_status)

Use filter to keep only respondents who are divorced or separated. Then, use select to show only the marital_status variable.

nhanes %>% 
  filter(marital_status == "Divorced" | marital_status == "Separated") %>% 
  select(marital_status)

Use %in% within the filter function to keep only those who are divorced, separated, or widowed. Then, use select to show only the marital_status variable.

nhanes %>% 
  filter(marital_status %in% c("Divorced", "Separated", "Widowed")) %>%
  select(marital_status)

We can chain together multiple filter functions. Doing it this way, we don’t have create complex logic in one line.

Create a chain that keeps only those are college grads (line #1). Then, filter to keep only those who are divorced, separated, or widowed. Finally, use select to show only the education and marital_status variables.

nhanes %>% 
  filter(education == "College Grad") %>% 
  filter(marital_status %in% c("Divorced", "Separated", "Widowed")) %>%
  select(education, marital_status)

We can use Use <, >, <=, and => for numeric data.

Use filter to only show those reported at least 5 days of physical activity in the last 30 days (this is the phys_active_days variable). Then, use select to keep only the phys_active_days and the days_phys_hlth_bad variables.

nhanes %>% 
  filter(phys_active_days >= 5) %>% 
  select(phys_active_days, days_phys_hlth_bad)

We can drop NAs with !is.na

Do the same thing as above, but drop responses that don’t have a response for days_phys_hlth_bad. Then, use select to keep only the phys_active_days and the days_phys_hlth_bad variables.

nhanes %>% 
  filter(phys_active_days >= 5) %>% 
  filter(!is.na(days_phys_hlth_bad)) %>% 
  select(phys_active_days, days_phys_hlth_bad)

You can also drop NAs with drop_na

Do the same thing as above, but use drop_na instead of !is.na. Make sure you get the same result!

nhanes %>% 
  filter(phys_active_days >= 5) %>% 
  drop_na(days_phys_hlth_bad) %>% 
  select(phys_active_days, days_phys_hlth_bad)

summarize

With summarize, we can go from a complete dataset down to a summary.

Get the mean hours of sleep per night that respondents say they get.

nhanes %>% 
  summarize(mean_hours_sleep = mean(sleep_hrs_night, na.rm = TRUE))

We can have multiple arguments in each usage of summarize.

In addition to calculating the mean hours of sleep per night, calculate the number of responses.

nhanes %>% 
  summarize(mean_hours_sleep = mean(sleep_hrs_night, na.rm = TRUE),
            number_of_responses = n())

group_by

summarize becomes truly powerful when paired with group_by, which enables us to perform calculations on each group.

Calculate the mean hours of sleep for females and males using group_by and summarize.

nhanes %>% 
  group_by(gender) %>% 
  summarize(mean_hours_sleep = mean(sleep_hrs_night, na.rm = TRUE))

We can use group_by with multiple groups.

Use group_by for gender and work (whether or not respondents are working) before calculating mean hours of sleep.

nhanes %>% 
  group_by(gender, work) %>% 
  summarize(mean_hours_sleep = mean(sleep_hrs_night, na.rm = TRUE),
            number_of_observations = n())

count

If we just want to count the number of things per group, we can use count.

Use count to show the number of responses by highest level of education completed (education).

nhanes %>% 
  count(education) 

We can also count by multiple groups.

Use count to show the number of responses for education and phys_active.

nhanes %>% 
  count(education, phys_active) 

arrange

With arrange, we can reorder rows in a data frame based on the values of one or more variables. R arranges in ascending order by default.

Use count to show the number of responses by education level. Then, use arrange to order by the number of responses.

nhanes %>% 
  count(education) %>% 
  arrange(n)

We can also arrange in descending order using desc.

Do the same thing as above, but put it in descending order using desc.

nhanes %>% 
  count(education) %>% 
  arrange(desc(n)) 

We often use arrange at the end of chains to display things in order.

Create a chain that does the following:

  1. Uses filter to only include those age 30 or older
  2. Uses group_by to create a male and female group
  3. Uses summarize to calculate a new variable called mean_bad_mental_health_days for males and females
  4. Uses mutate to round mean_bad_mental_health_days to one decimal place
  5. Uses arrange to put mean_bad_mental_health_days in descending order
nhanes %>% 
  filter(age >= 30) %>% 
  group_by(gender) %>% 
  summarize(mean_bad_mental_health_days = mean(days_ment_hlth_bad, na.rm = TRUE)) %>% 
  mutate(mean_bad_mental_health_days = round(mean_bad_mental_health_days, digits = 1)) %>% 
  arrange(desc(mean_bad_mental_health_days))

Create a new data frame

Sometimes you want to save the results of your work to a new data frame.

Copy the code above and save it to a new data frame called mental_health_over_30.

mental_health_over_30 <- nhanes %>% 
  filter(age >= 30) %>% 
  group_by(gender) %>% 
  summarize(mean_bad_mental_health_days = mean(days_ment_hlth_bad, na.rm = TRUE)) %>% 
  mutate(mean_bad_mental_health_days = round(mean_bad_mental_health_days, digits = 1)) %>% 
  arrange(desc(mean_bad_mental_health_days))

Crosstabs

Sometimes you want your results in a crosstab. We can use the tabyl function in janitor package to make crosstabs automatically.

Create a crosstab of gender and health_gen.

nhanes %>% 
  tabyl(gender, health_gen) 

Add a drop_na before your line with tabyl to get rid of all NAs.

nhanes %>% 
  drop_na(gender, health_gen) %>% 
  tabyl(gender, health_gen) 

janitor has a set of functions that all start with adorn_ that add a number of things to our crosstabs. We call them after tabyl. For example, adorn_totals.

Use the code above and then add totals using adorn_totals in the rows and columns.

nhanes %>% 
  drop_na(gender, health_gen) %>% 
  tabyl(gender, health_gen) %>% 
  adorn_totals(where = c("row", "col"))

We can add adorn_percentages to add percentages.

Use the code above and then add percentages using adorn_percentages.

nhanes %>% 
  drop_na(gender, health_gen) %>% 
  tabyl(gender, health_gen) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages()

We can then format these percentages using adorn_pct_formatting.

Use the code above and then format the percentages using adorn_pct_formatting. Add arguments so that the percentages are rounded to 1 digit. Note that R uses the “half to even” rounding method by default so if you want to round, say, 14.5 to 15 you must use the rounding argument (type ?adorn_pct_formatting in the console to learn more).

nhanes %>% 
  drop_na(gender, health_gen) %>% 
  tabyl(gender, health_gen) %>% 
  adorn_totals(where = c("row", "col")) %>% 
  adorn_percentages() %>% 
  adorn_pct_formatting(digits = 1, 
                       rounding = "half up") 

If we want to include the n alongside percentages, we can use adorn_ns.

Use the code above and then add a line with adorn_ns to include the n.

nhanes %>% 
  drop_na(gender, health_gen) %>% 
  tabyl(gender, health_gen) %>% 
  adorn_totals(c("row", "col")) %>% 
  adorn_percentages() %>% 
  adorn_pct_formatting(digits = 1, 
                       rounding = "half up") %>% 
  adorn_ns()

We can add titles to our crosstabs using adorn_title.

Use the code above and then add a title using adorn_title. Use the placement argument and see what you get.

nhanes %>% 
  drop_na(gender, health_gen) %>% 
  tabyl(gender, health_gen) %>% 
  adorn_totals(c("row", "col")) %>% 
  adorn_percentages() %>% 
  adorn_pct_formatting(digits = 0, 
                       rounding = "half up") %>% 
  adorn_ns() %>% 
  adorn_title(placement = "combined")

We can also do three (or more) way crosstabs automatically by adding more variables to the tabyl function.

Use the code above, but add a third variable (age_decade) to the line with drop_na and the line with tabyl. You should get a series of crosstabs.

nhanes %>% 
  drop_na(gender, health_gen, age_decade) %>% 
  tabyl(gender, health_gen, age_decade) %>% 
  adorn_totals(c("row", "col")) %>% 
  adorn_percentages() %>% 
  adorn_pct_formatting(digits = 0, rounding = "half up") %>% 
  adorn_ns() %>% 
  adorn_title()
## $`10-19`
##         health_gen                                               
##  gender  Excellent    Fair      Good   Poor     Vgood       Total
##  female   9%  (46) 9% (47) 44% (223) 1% (4) 37% (190) 100%  (510)
##    male  17%  (94) 7% (40) 33% (181) 1% (3) 42% (233) 100%  (551)
##   Total  13% (140) 8% (87) 38% (404) 1% (7) 40% (423) 100% (1061)
## 
## $`20-29`
##         health_gen                                                  
##  gender  Excellent      Fair      Good    Poor     Vgood       Total
##  female  12%  (69) 13%  (76) 38% (219) 2% (11) 34% (197) 100%  (572)
##    male  15%  (93) 11%  (66) 40% (244) 1%  (6) 34% (208) 100%  (617)
##   Total  14% (162) 12% (142) 39% (463) 1% (17) 34% (405) 100% (1189)
## 
## $`30-39`
##         health_gen                                                  
##  gender  Excellent      Fair      Good    Poor     Vgood       Total
##  female  12%  (66) 15%  (82) 41% (229) 2%  (9) 31% (173) 100%  (559)
##    male  11%  (63) 10%  (59) 45% (265) 0%  (2) 34% (197) 100%  (586)
##   Total  11% (129) 12% (141) 43% (494) 1% (11) 32% (370) 100% (1145)
## 
## $`40-49`
##         health_gen                                                  
##  gender  Excellent      Fair      Good    Poor     Vgood       Total
##  female  12%  (68) 13%  (76) 41% (240) 2% (12) 33% (192) 100%  (588)
##    male  11%  (69) 17% (108) 42% (275) 2% (10) 29% (190) 100%  (652)
##   Total  11% (137) 15% (184) 42% (515) 2% (22) 31% (382) 100% (1240)
## 
## $`50-59`
##         health_gen                                                  
##  gender  Excellent      Fair      Good    Poor     Vgood       Total
##  female   9%  (47) 14%  (75) 32% (174) 5% (29) 41% (224) 100%  (549)
##    male  10%  (61) 20% (126) 39% (248) 4% (27) 28% (177) 100%  (639)
##   Total   9% (108) 17% (201) 36% (422) 5% (56) 34% (401) 100% (1188)
## 
## $`60-69`
##         health_gen                                                 
##  gender  Excellent      Fair      Good    Poor     Vgood      Total
##  female  14%  (62) 13%  (60) 34% (151) 4% (19) 35% (156) 100% (448)
##    male  13%  (54) 12%  (52) 41% (170) 5% (19) 30% (124) 100% (419)
##   Total  13% (116) 13% (112) 37% (321) 4% (38) 32% (280) 100% (867)
## 
## $`70+`
##         health_gen                                                
##  gender  Excellent     Fair      Good    Poor     Vgood      Total
##  female    7% (24) 15% (47) 40% (130) 4% (12) 34% (108) 100% (321)
##    male   14% (31) 18% (39) 36%  (78) 5% (11) 27%  (60) 100% (219)
##   Total   10% (55) 16% (86) 39% (208) 4% (23) 31% (168) 100% (540)