In Assignment 1, you practiced the basics of using R and Posit Cloud. You learned how to run code, create objects, use functions, and knit an R Markdown file.
In this assignment, we will use R for a real biostatistics task: describing one variable at a time. This is called univariate exploratory data analysis, or univariate EDA.
The statistics ideas in this assignment should look familiar from lecture. The main purpose of this lab is to learn how to make R do the work: import a dataset, inspect variables, calculate summaries, make graphs, and write short interpretations based on the output.
By the end of this assignment, you should be able to:
This assignment uses the dataset:
patient_descriptive_data.csv
Each row in the dataset represents one fictional participant.
In Assignment 1, you used basic R commands. In this assignment, we will use a collection of R packages called the tidyverse.
A package is an add-on for R. Packages give R extra tools. The tidyverse is a collection of packages that are commonly used for data analysis. In this assignment, tidyverse tools will help us read the dataset, summarize variables, and make graphs.
You will see code that uses the pipe symbol:
You can read the pipe as:
and then
For example, this code:
can be read as:
Start with
patient_data, and then count the values ofSmokingStatus.
You do not need to master every detail of the tidyverse right now. The goal is to use a small number of R tools to describe data clearly.
When describing one variable, a useful workflow is:
The type of variable determines the tools we use.
| Variable Type | Useful Summaries | Useful Graphs |
|---|---|---|
| Numeric | Mean, median, standard deviation, IQR | Histogram, boxplot |
| Categorical | Counts, percentages | Bar chart |
This assignment will walk you through that workflow.
For each task, first run the worked example in Part A: I Do. Then complete the similar practice task in Part B: You Do.
Use the output from the Part B chunks to answer the quiz questions in eLC. You should not need to write additional code for the quiz beyond what the assignment already asks you to run.
When you are finished, click Knit to create your HTML file.
Before we can analyze data, we need to load the tools and the dataset.
Run the code below.
The first line loads the tidyverse:
The second line reads the CSV file and stores it in an object called
patient_data:
The arrow <- means that we are saving something. In
this case, we are saving the dataset into an object called
patient_data.
Now print the dataset.
## # A tibble: 30 × 12
## ParticipantID Age Sex Race WeightKg HeightCm BMI SystolicBP
## <dbl> <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 1001 24 Female White 66.2 165. 24.3 118
## 2 1002 31 Male Black 88.5 178. 28 132
## 3 1003 45 Female White 71.4 163. 27 126
## 4 1004 52 Male Hispanic 95.1 180. 29.3 145
## 5 1005 37 Female Asian 58.9 159. 23.4 112
## 6 1006 60 Female Black 82.7 168. NA 150
## 7 1007 29 Male White 79.3 175. 25.8 124
## 8 1008 41 Female Hispanic 69.8 160 27.3 136
## 9 1009 55 Male White 102. 183. 30.6 158
## 10 1010 34 Female White 64.5 164 24 120
## # ℹ 20 more rows
## # ℹ 4 more variables: DiastolicBP <dbl>, HeartRate <dbl>, SmokingStatus <chr>,
## # ExerciseDays <dbl>
When R prints this dataset, it appears as a tibble. A tibble is a tidyverse version of a data table. It shows the variable names, variable types, and the first few rows of the dataset.
Print the first few rows of the dataset using
head().
## # A tibble: 3 × 12
## ParticipantID Age Sex Race WeightKg HeightCm BMI SystolicBP DiastolicBP
## <dbl> <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1001 24 Fema… White 66.2 165. 24.3 118 76
## 2 1002 31 Male Black 88.5 178. 28 132 84
## 3 1003 45 Fema… White 71.4 163. 27 126 82
## # ℹ 3 more variables: HeartRate <dbl>, SmokingStatus <chr>, ExerciseDays <dbl>
The function head() shows the first few rows of a
dataset. This is a quick way to make sure the data loaded correctly.
Before summarizing data, we should inspect the dataset.
The glimpse() function gives a quick overview of the
variables.
Run the code below.
## Rows: 30
## Columns: 12
## $ ParticipantID <dbl> 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 10…
## $ Age <dbl> 24, 31, 45, 52, 37, 60, 29, 41, 55, 34, 48, 27, 39, 63, …
## $ Sex <chr> "Female", "Male", "Female", "Male", "Female", "Female", …
## $ Race <chr> "White", "Black", "White", "Hispanic", "Asian", "Black",…
## $ WeightKg <dbl> 66.2, 88.5, 71.4, 95.1, 58.9, 82.7, 79.3, 69.8, 102.4, 6…
## $ HeightCm <dbl> 165.1, 177.8, 162.6, 180.3, 158.8, 167.6, 175.3, 160.0, …
## $ BMI <dbl> 24.3, 28.0, 27.0, 29.3, 23.4, NA, 25.8, 27.3, 30.6, 24.0…
## $ SystolicBP <dbl> 118, 132, 126, 145, 112, 150, 124, 136, 158, 120, NA, 11…
## $ DiastolicBP <dbl> 76, 84, 82, 92, 70, 95, 78, 86, 96, 78, 88, 68, 82, 90, …
## $ HeartRate <dbl> 72, 78, 74, 86, 68, 88, 70, 76, 90, 72, 84, 66, 80, 82, …
## $ SmokingStatus <chr> "Never", "Former", "Never", "Current", "Never", "Former"…
## $ ExerciseDays <dbl> 3, 1, 4, 0, 5, 1, 2, 3, 0, 4, 1, 6, 2, 1, 2, 0, 5, 3, 4,…
The glimpse() function prints each variable name, shows
the variable type, and previews some of the values.
In the output, you may see types such as:
<dbl>: numeric variable<chr>: character variable, often used for
categoriesFor example, Age is numeric because it contains numbers.
SmokingStatus is categorical because it contains labels
such as "Never", "Former", and
"Current".
Use the glimpse() output to answer the questions
below.
Is BMI numeric or categorical?
Your answer:
## Rows: 30
## Columns: 1
## $ BMI <dbl> 24.3, 28.0, 27.0, 29.3, 23.4, NA, 25.8, 27.3, 30.6, 24.0, 30.2, 22…
BMI is numeric
Is Race numeric or categorical?
Your answer:
## Rows: 30
## Columns: 1
## $ Race <chr> "White", "Black", "White", "Hispanic", "Asian", "Black", "White",…
Race is categorical
Numeric variables can be summarized using measures of center and spread. In this section, we will use R to calculate the mean, median, standard deviation, and IQR.
Summarize BMI.
patient_data %>%
summarize(
mean_bmi = mean(BMI, na.rm = TRUE),
median_bmi = median(BMI, na.rm = TRUE),
sd_bmi = sd(BMI, na.rm = TRUE),
iqr_bmi = IQR(BMI, na.rm = TRUE)
)## # A tibble: 1 × 4
## mean_bmi median_bmi sd_bmi iqr_bmi
## <dbl> <dbl> <dbl> <dbl>
## 1 27.2 27.7 2.52 3.60
Let’s read this code carefully.
The first line starts with the dataset:
This says:
Start with
patient_data, and then…
The next line sends the dataset into summarize():
The summarize() function creates a small summary table.
Each line inside summarize() creates one summary value.
For example:
This line creates a summary value called mean_bmi.
The part on the left side of the equals sign names the output:
The part on the right side of the equals sign calculates the value:
The argument na.rm = TRUE tells R to remove missing
values before calculating the summary. This is important because some
variables in this dataset have missing values. If you forget
na.rm = TRUE, R may return NA instead of the
summary statistic.
Summarize WeightKg.
Calculate:
Use the names:
mean_weightmedian_weightsd_weightiqr_weight# Summarize WeightKg.
patient_data %>%
summarize(
mean_weight = mean(WeightKg, na.rm = TRUE),
median_weight = median(WeightKg, na.rm = TRUE),
sd_weight = sd(WeightKg, na.rm = TRUE),
iqr_weight = IQR(WeightKg, na.rm = TRUE)
)## # A tibble: 1 × 4
## mean_weight median_weight sd_weight iqr_weight
## <dbl> <dbl> <dbl> <dbl>
## 1 78.7 78.4 12.0 18.7
Sometimes we also want the minimum, maximum, and quartiles. These help us understand the overall range and the middle part of the distribution.
Calculate several summaries for BMI.
patient_data %>%
summarize(
min_bmi = min(BMI, na.rm = TRUE),
q1_bmi = quantile(BMI, 0.25, na.rm = TRUE),
median_bmi = median(BMI, na.rm = TRUE),
q3_bmi = quantile(BMI, 0.75, na.rm = TRUE),
max_bmi = max(BMI, na.rm = TRUE)
)## # A tibble: 1 × 5
## min_bmi q1_bmi median_bmi q3_bmi max_bmi
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 22.4 25.6 27.7 29.2 30.8
This code uses the same summarize() structure as Part 3.
The only difference is that we are asking R to calculate different
summaries.
The function min() finds the smallest value.
The function max() finds the largest value.
The function quantile() finds a percentile. For
example:
calculates the first quartile, or Q1, for BMI.
Calculate the minimum, Q1, median, Q3, and maximum for
SystolicBP.
Use the names:
min_sbpq1_sbpmedian_sbpq3_sbpmax_sbp# Calculate min, Q1, median, Q3, and max for SystolicBP.
patient_data %>%
summarize(
min_sbp = min(SystolicBP, na.rm = TRUE),
q1_sbp = quantile(SystolicBP, 0.25, na.rm = TRUE),
median_sbp = median(SystolicBP, na.rm = TRUE),
q3_sbp = quantile(SystolicBP, 0.75, na.rm = TRUE),
max_sbp = max(SystolicBP, na.rm = TRUE)
)## # A tibble: 1 × 5
## min_sbp q1_sbp median_sbp q3_sbp max_sbp
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 110 124 134 145 158
A histogram is useful for graphing a numeric variable. In this
section, the main goal is to learn the R code pattern for creating one
with ggplot().
Create a histogram of BMI.
Let’s read the code.
The ggplot() function starts the graph:
The argument data = patient_data tells R which dataset
to use.
The part aes(x = BMI) tells R to put BMI on
the x-axis. The aes() function is where we tell ggplot
which variables to use in the graph.
The next line adds the histogram:
The geom_histogram() function tells R to create a
histogram. The argument bins = 8 tells R to use 8 bars.
The plus sign + connects the pieces of the graph. In
ggplot, we build graphs by starting with ggplot() and then
adding layers with +.
A graph should have clear labels so the reader knows what is being shown.
We add labels using labs().
Create a labeled histogram of BMI.
ggplot(data = patient_data, aes(x = BMI)) +
geom_histogram(bins = 8) +
labs(
title = "Distribution of BMI",
x = "Body Mass Index",
y = "Number of Participants"
)This code has three pieces.
The first piece starts the graph and chooses the variable:
The second piece adds the histogram:
The third piece adds labels:
Inside labs(), the words in quotation marks are the
labels that appear on the graph.
Create a labeled histogram of WeightKg.
Use the following labels:
"Distribution of Weight""Weight in Kilograms""Number of Participants"# Create a labeled histogram of WeightKg.
ggplot(data = patient_data, aes(x = WeightKg)) +
geom_histogram(bins = 8) +
labs(
title = "Distribution of Weight",
x = "Weight in Kilograms",
y = "Number of Participants"
)A boxplot is another useful graph for a numeric variable. It gives a compact picture of the median, spread, and possible unusual values.
Create a boxplot of BMI.
This code uses the same ggplot structure.
The first line starts the graph and puts BMI on the
x-axis.
The second line adds the boxplot layer:
Now we will combine the numeric summaries and graphs.
A good interpretation should mention a typical value, the amount of spread, and one pattern from the graph. You do not need to write a long paragraph. Two or three clear sentences are enough.
A reasonable interpretation of BMI might be:
The typical BMI in this sample is in the upper 20s, based on the mean and median. The BMI values show moderate spread, with most values falling from the low 20s to around 30. The histogram and boxplot suggest that BMI is concentrated mostly in the mid-to-upper 20s, with a few higher values.
This interpretation uses both numbers and graphs. It does not overstate the result.
Using your summaries, histogram, and boxplot, write two or three
sentences describing WeightKg.
Your answer: > The typical WeightKg in this sample is above 65kgs, based on the mean and median. The WeightKg values show moderate spread, with most values falling from around 65kg to 95kg. The histogram suggest that WeightKg is concentrated mostly in around 65kg to 75kg, with fewer in higher values.
Categorical variables are summarized using counts and percentages.
Count the number of participants in each SmokingStatus
category.
## # A tibble: 3 × 2
## SmokingStatus n
## <chr> <int>
## 1 Current 4
## 2 Former 8
## 3 Never 18
The code starts with the dataset:
Then the pipe sends the dataset into count():
The count() function counts how many observations are in
each category of SmokingStatus.
Now add percentages.
## # A tibble: 3 × 3
## SmokingStatus n percent
## <chr> <int> <dbl>
## 1 Current 4 13.3
## 2 Former 8 26.7
## 3 Never 18 60
This code has one additional step:
The mutate() function creates a new column.
Here, the new column is called percent.
The expression n / sum(n) * 100 calculates the
percentage in each category. The column n was created by
count(), and sum(n) gives the total number of
participants counted across all categories.
Count the number of participants in each Race category
and add percentages.
# Count Race categories and add percentages.
patient_data %>%
count(Race) %>%
mutate(percent = n / sum(n) * 100)## # A tibble: 4 × 3
## Race n percent
## <chr> <int> <dbl>
## 1 Asian 4 13.3
## 2 Black 7 23.3
## 3 Hispanic 6 20
## 4 White 13 43.3
A bar chart is useful for graphing a categorical variable.
Create a bar chart of SmokingStatus.
This code follows the same ggplot pattern as the histogram and boxplot.
The first line starts the graph and puts SmokingStatus
on the x-axis.
The second line adds the bar chart:
The geom_bar() function counts how many observations are
in each category and creates the bars.
As with histograms, bar charts should have clear labels.
Create a labeled bar chart of SmokingStatus.
ggplot(data = patient_data, aes(x = SmokingStatus)) +
geom_bar() +
labs(
title = "Smoking Status of Participants",
x = "Smoking Status",
y = "Number of Participants"
)The graph starts with ggplot(), adds the bar chart with
geom_bar(), and adds the title and axis labels with
labs().
Create a labeled bar chart of Race.
Use the following labels:
"Race Categories of Participants""Race""Number of Participants"# Create a labeled bar chart of Race.
ggplot(data = patient_data, aes(x = Race)) +
geom_bar() +
labs(
title = "Race Categories of Participants",
x = "Race",
y = "Number of Participants"
)A good interpretation of a categorical variable should mention the most common category, the count or percentage in that category, and any categories that are less common.
A reasonable interpretation of SmokingStatus might
be:
The most common smoking status in this sample is never smoker. There are 18 never smokers, which is 60% of the sample. Former and current smokers are less common in this dataset.
This interpretation uses counts and percentages.
Using your count table, percentage table, and bar chart, write two or
three sentences describing Race.
Your answer: > The most common race category in this sample is white. There are 13 white people, which is 43.33% of the sample. Asian, Hispanic, and Black in total are contributing to more than half of the population in this dataset.
Now put the pieces together.
Write a short paragraph describing WeightKg.
Include:
Your answer: > The typical WeightKg in this sample is above 65kgs, based on the mean and median. The WeightKg values show moderate spread, with most values falling from around 65kg to 95kg. The histogram suggest that WeightKg is concentrated mostly in around 65kg to 75kg, with fewer in higher values.
Write a short paragraph describing Race.
Include:
Your answer: > The most common race category in this sample is white. There are 13 white people, which is 43.33% of the sample. Asian, Hispanic, and Black in total are contributing to more than half of the population in this dataset.
For each variable below, identify an appropriate summary and graph.
Age: | Numeric | Mean, median, standard deviation,
IQR | Histogram, boxplot |
SmokingStatus: | Categorical | Counts, percentages |
Bar chart |
SystolicBP: | Numeric | Mean, median, standard
deviation, IQR | Histogram, boxplot |
Sex: | Categorical | Counts, percentages | Bar chart
|
Use your output from the code chunks above to answer the quiz questions in eLC. You should not need to write any new code to answer these questions.
From Part 2B, is BMI numeric or categorical?
A. Numeric
B. Categorical
C. Both numeric and categorical
D. Neither numeric nor categorical
Answer:A
From Part 2B, is Race numeric or categorical?
A. Numeric
B. Categorical
C. Both numeric and categorical
D. Neither numeric nor categorical
Answer:B
In the Part 3A example, what does na.rm = TRUE tell R to
do?
A. Rename the variable before calculating the summary.
B. Remove missing values before calculating the summary.
C. Remove duplicate rows before calculating the summary.
D. Round the answer to two decimal places.
Answer:B
From Part 3B, approximately what is the mean of
WeightKg?
A. 69.80
B. 78.69
C. 78.40
D. 88.50
Answer:B
From Part 3B, what is the median of WeightKg?
A. 69.80
B. 78.40
C. 78.69
D. 88.50
Answer:B
From Part 3B, approximately what is the standard deviation of
WeightKg?
A. 2.52
B. 11.96
C. 18.70
D. 78.69
Answer:B
From Part 3B, what is the IQR of WeightKg?
A. 11.96
B. 18.70
C. 69.80
D. 88.50
Answer:B
From Part 4B, what is the median of SystolicBP?
A. 124
B. 134
C. 145
D. 158
Answer:B
From Part 4B, what is the third quartile, or Q3, of
SystolicBP?
A. 110
B. 124
C. 134
D. 145
Answer:D
Which summary-and-graph combination is appropriate for a numeric
variable such as WeightKg?
A. Counts, percentages, and a bar chart
B. Mean, median, standard deviation, IQR, histogram, and boxplot
C. Counts, percentages, histogram, and boxplot
D. Mean, median, standard deviation, IQR, and bar chart
Answer:B
Which ggplot function creates a histogram?
A. geom_bar()
B. geom_boxplot()
C. geom_histogram()
D. geom_hist()
Answer:C
Which ggplot function creates a boxplot?
A. geom_bar()
B. geom_boxplot()
C. geom_histogram()
D. geom_box()
Answer:B
From Part 9B, which Race category has the largest
count?
A. Asian
B. Black
C. Hispanic
D. White
Answer:D
From Part 9B, how many participants are in the largest
Race category?
A. 4
B. 6
C. 7
D. 13
Answer:D
From Part 9B, approximately what percentage of participants are in
the largest Race category?
A. 13.3%
B. 20.0%
C. 23.3%
D. 43.3%
Answer:D
Which ggplot function creates a bar chart?
A. geom_bar()
B. geom_boxplot()
C. geom_histogram()
D. geom_barchart()
Answer:A
In the code mutate(percent = n / sum(n) * 100), what new
column is being created?
A. n
B. Race
C. percent
D. sum
Answer:C
In this assignment, how should you read the pipe symbol
%>%?
A. equals
B. greater than
C. and then
D. divided by
Answer:C
Which summary-and-graph combination is appropriate for a categorical
variable such as SmokingStatus?
A. Mean, median, standard deviation, and histogram
B. Mean, median, IQR, and boxplot
C. Counts, percentages, and a bar chart
D. Standard deviation, IQR, and a bar chart
Answer:C
Which interpretation is most consistent with the Race
summary from Part 9B?
A. White is the most common race category in the sample, and Asian is
the least common.
B. Asian is the most common race category in the sample, and White is
the least common.
C. All race categories have exactly the same number of
participants.
D. Race should be summarized using a mean and standard deviation.
Answer:A
Click the Knit button to create your HTML file.
Before submitting, check that:
Error messages are part of learning R. Read them carefully. Check the named code chunk. Revise your code one step at a time.
The goal is not to memorize every command immediately. The goal is to learn how R can help you describe data clearly.
In this assignment, you described one variable at a time using numerical summaries, histograms, boxplots, counts, percentages, and bar charts.