Homework 2

Descriptive Statistics and Reading and Transforming Data with R

Homework 2 questions based on Lectures 4 and 5.
Author

Byeong-Hak Choe

Published

September 30, 2026

Homework 2 Question Scores

The table summarizes earned points divided by the maximum points for each question, on a 0–1 scale. A score of 1 means full credit; 0.75 and 0.50 indicate partial credit. The mean score shows the average fraction of credit earned, not the percentage of students who answered perfectly.

Instructions

This homework covers Lectures 4 and 5. Answer all 35 questions: Questions 1–17 are conceptual questions (14 multiple-choice questions and three fill-in-the-blank questions), and Questions 18–35 ask you to fill in one R-code blank.

  • For each multiple-choice question, select the one best answer.
  • For each conceptual fill-in-the-blank question, enter only the requested term.
  • For each coding question, enter only the R code that replaces [?], not the full line or code block. Each question has one blank.
  • Run the data-loading code below once, then write and test your answers in an R script before submitting them.
  • Submit your answers through Brightspace. Check Brightspace for the due date.

For Questions 21–35, use this data frame. Questions 18–20 use the local-file scenario stated in Part III.

library(tidyverse)

# URL for the CSV file
CSV_url <- 'https://bcdanl.github.io/data/custdata_rev.csv'
custdata <- read_csv(CSV_url)

Variable descriptions

Each row represents one customer.

Variable Description
custid Unique customer identifier, stored as text.
sex Reported sex: Female or Male.
is_employed Employment status: TRUE = employed; FALSE = unemployed; NA = unknown or not applicable.
income Customer income in U.S. dollars.
marital_status Marital status, such as married, never married, divorced/separated, or widowed.
housing_type Housing situation, such as renting or owning a home.
recent_move Whether the customer moved in the past year: TRUE = yes; FALSE = no.
num_vehicles Number of vehicles in the household; 6 means six or more.
age Customer age in years.
state_of_res State of residence, including the District of Columbia.
gas_usage Monthly gas-bill field; values 4–999 represent dollar amounts, with special codes below.
health_ins Health-insurance status: TRUE = insured; FALSE = uninsured.

For gas_usage, 1 means included in rent or condo fees, 2 means included in the electricity payment, and 3 means no charge or gas not used. The gas questions use the recorded values. In any column, NA represents a missing or not-applicable value.

Reference: Customer data dictionary.

Part I: Multiple Choice

Question 1

Group A has ages 29, 30, and 31. Group B has ages 20, 30, and 40. Both groups have a mean age of 30. Which statement is correct?

  1. Group A has greater variability because its ages are closer together.
  2. Group B has greater variability because its ages are farther from the mean.
  3. The groups have equal variability because their means are equal.
  4. Variability cannot be compared unless the groups have different medians.

b. Group B has greater variability because its ages are farther from the mean. Both means are 30, but Group B’s deviations from 30 are larger.

Question 2

The mean income in a customer dataset is higher this year than last year. What does this descriptive comparison establish by itself?

  1. A new marketing program caused the increase.
  2. Every customer’s income increased.
  3. The observed mean is higher, but the reason for the change is not established.
  4. The median income must also have increased.

c. The observed mean is higher, but the reason for the change is not established. A descriptive comparison does not by itself identify a cause or establish what happened to every customer.

Question 3

A customer’s gas_usage value appears beyond the upper fence of a boxplot. What is the most appropriate first interpretation?

  1. The value is certainly a data-entry error and must be deleted.
  2. The value is a possible outlier that should be checked in context.
  3. The value must be replaced by the mean gas usage.
  4. The value proves that all other gas-usage records are correct.

b. The value is a possible outlier that should be checked in context. A point beyond a boxplot fence may be unusual without being erroneous.

Question 4

For x <- c(4, 10, 18), what does R return from range(x)?

  1. c(4, 18), the minimum and maximum
  2. 14, the width from minimum to maximum
  3. 32, the sum of the values
  4. c(10, 14), the median and range width

a. c(4, 18), the minimum and maximum. R’s range(x) returns both endpoints; the range width is 18-4=14.

Question 5

Suppose the third quartile (Q_3) of customer age is 62 years. Which interpretation is best?

  1. About 25% of ages are at or below 62.
  2. About 50% of ages are at or below 62.
  3. About 75% of ages are at or below 62.
  4. Every customer is older than 62.

c. About 75% of ages are at or below 62. Q_3 is the 75th-percentile cut point. Ties may make the exact fraction differ from 75%.

Question 6

Ten customers each spend 20 dollars, and 90 customers each spend 50 dollars. Which expression gives the mean spending per customer across all 100 customers?

  1. (20+50)/2
  2. (10\times20+90\times50)/(10+90)
  3. (10+90)/(20+50)
  4. 20\times50

b. (10\times20+90\times50)/(10+90). The groups must be weighted by their customer counts. The result is 47 dollars per customer.

Question 7

Which description of a CSV file is correct?

  1. It is an R-only file that cannot be opened outside R.
  2. It is a plain-text table whose values are separated by commas; its first line often contains column names.
  3. It stores only one numeric vector and no column names.
  4. It automatically records the meaning of every variable and every missing value.

b. It is a plain-text table whose values are separated by commas; its first line often contains column names. A CSV does not automatically document variable meanings.

Question 8

If custdata is organized as tidy data with one row per customer, which description is correct?

  1. Each column is a customer, and each row is a variable.
  2. Each row is a customer, each column is a variable, and each cell holds one value.
  3. Each cell is a customer, and each row holds a complete variable.
  4. Each row must contain only numeric values.

b. Each row is a customer, each column is a variable, and each cell holds one value. This is the tidy-data layout for the stated observational unit.

Question 9

What does the native pipe |> do in custdata |> filter(age >= 65)?

  1. It assigns filter to the name custdata.
  2. It passes custdata as the first input to filter(), equivalent to filter(custdata, age >= 65).
  3. It joins custdata to a second data frame.
  4. It changes every age to 65.

b. It passes custdata as the first input to filter(). The piped expression is equivalent to filter(custdata, age >= 65).

Question 10

You want to keep only rows for customers aged 65 or older, then keep only the custid and age columns. Which functions perform these two tasks, in order?

  1. select(), then filter()
  2. filter(), then select()
  3. arrange(), then distinct()
  4. rename(), then arrange()

b. filter(), then select(). filter() keeps rows meeting the age condition; select() keeps the requested columns.

Question 11

A filter should keep customers who live in New York or New Jersey. Which operator combines the two state comparisons correctly?

  1. &, because each row must match both states
  2. |, because each row may match either state
  3. !, because each row must match neither state
  4. ==, because it combines two complete logical conditions

b. |, because each row may match either state. An & condition would require the same state value to equal both names simultaneously.

Question 12

Some values of is_employed are missing. Why should you use is.na(is_employed) rather than is_employed == NA to find them?

  1. NA is the same as FALSE.
  2. Comparing a value with NA using == returns NA, not a useful TRUE/FALSE missingness test.
  3. is.na() replaces missing values with zero.
  4. == NA works only when the column is numeric.

b. Comparing a value with NA using == returns NA, not a useful TRUE/FALSE missingness test. is.na(is_employed) identifies which values are missing.

Question 13

How does arrange(custdata, state_of_res, desc(age)) order the rows?

  1. By age from oldest to youngest, then by state only when ages tie
  2. By state alphabetically; within each state, by age from oldest to youngest
  3. By state alphabetically; within each state, by age from youngest to oldest
  4. It removes rows with repeated states.

b. By state alphabetically; within each state, by age from oldest to youngest. arrange() uses the first variable as the primary sort key, and desc(age) breaks state ties in descending age order.

Question 14

What does distinct(custdata, state_of_res) return?

  1. One row for each unique state value, with a state_of_res column
  2. The number of customers in each state
  3. All original customer rows, sorted by state
  4. Only customers with missing state values

a. One row for each unique state value, with a state_of_res column. distinct(custdata, state_of_res) returns the unique values of that selected column, not customer counts.

Part II: Conceptual Fill in the Blanks

For Questions 15–17, consider a new Posit Cloud project whose working directory has not been changed from the default, /cloud/project. A copy of custdata_rev.csv has been uploaded to a folder named data_files directly inside the project folder.

Question 15

R uses /cloud/project as the starting folder for interpreting relative pathnames. This starting folder is called R’s [Blank].

Enter only the two-word term that replaces the blank.

Working directory

In the stated Posit Cloud scenario, /cloud/project is the starting folder for relative pathnames.

Question 16

The pathname /cloud/project/data_files/custdata_rev.csv gives the file’s full location, beginning at the root folder /. It is an [Blank] pathname.

Enter only the term that replaces the blank.

Absolute

/cloud/project/data_files/custdata_rev.csv begins at the root folder / and gives the file’s full location.

Question 17

With R’s working directory set to /cloud/project, the pathname data_files/custdata_rev.csv locates the file starting from that working directory. It is a [Blank] pathname.

Enter only the term that replaces the blank.

Relative

data_files/custdata_rev.csv is interpreted from the working directory /cloud/project.

Part III: R Programming — Fill in the Code Blank

For Questions 18–20, use the same local-file scenario: R’s working directory is /cloud/project, and a copy of custdata_rev.csv is in the project’s data_files folder. Complete the code using these stated locations; you do not need to upload a file to answer these questions. Reading the CSV from the web in the Instructions does not save a local copy. For Questions 21–35, use the custdata data frame loaded from the web in the Instructions.

Question 18

Use an R function to obtain the current working directory. Enter only the function call that replaces [?].

current_folder <- [?]

getwd()

In the stated scenario, the returned pathname is /cloud/project.

Question 19

Read the local CSV using its absolute pathname. Enter only the pathname that replaces [?], including quotation marks.

custdata_absolute <- read_csv([?])

"/cloud/project/data_files/custdata_rev.csv"

custdata_absolute <- read_csv("/cloud/project/data_files/custdata_rev.csv")

The absolute pathname begins at /. Single quotation marks are also valid.

Question 20

Read the same local CSV using its relative pathname from /cloud/project. Enter only the pathname that replaces [?], including quotation marks.

custdata_relative <- read_csv([?])

"data_files/custdata_rev.csv"

custdata_relative <- read_csv("data_files/custdata_rev.csv")

R starts at /cloud/project and looks in its data_files subfolder. Single quotation marks are also valid.

Question 21

Count the number of rows in custdata. Enter only the code that replaces [?].

row_count <- [?]

nrow(custdata)

The data frame has 73,262 rows.

Question 22

Extract the num_vehicles column as a vector. Enter only the code that replaces [?].

vehicle_counts <- [?]

custdata$num_vehicles

The $ operator extracts the column as a vector.

Question 23

For this question, use the custdata_filtered data frame created below.

First, keep only rows where gas_usage is greater than or equal to 4, since values of 1, 2, and 3 represent categories rather than dollar amounts.

custdata_filtered <- custdata |> 
  filter(gas_usage >= 4)

Using the resulting custdata_filtered data frame, calculate the mean of gas_usage, excluding missing values. Enter only the code that replaces [?].

mean_gas_usage <- [?]

mean(custdata_filtered$gas_usage, na.rm = TRUE)

The mean is approximately 76.01. Use custdata_filtered, which keeps only recorded gas-usage values greater than or equal to 4. The na.rm = TRUE argument excludes any missing values from the calculation.

Question 24

Calculate the third quartile (Q_3) of age using quantile(). Enter only the code that replaces [?].

third_quartile_age <- [?]

quantile(custdata$age, probs = 0.75)

The third quartile is 62 years. quantile(custdata$age, 0.75) is equivalent.

Question 25

Calculate the interquartile range of income. Enter only the code that replaces [?].

income_iqr <- [?]

IQR(custdata$income)

The income IQR is 41,000.

Question 26

Calculate the sample standard deviation of income. Enter only the code that replaces [?].

income_sd <- [?]

sd(custdata$income)

The sample standard deviation is approximately 58,113.76, in the same units as income.

Question 27

Keep rows for female customers aged 65 or older. Enter only the condition or conditions that replace [?].

older_women <- custdata |>
  filter([?])

sex == "Female" & age >= 65

The completed filter retains 8,684 rows. Within filter(), sex == "Female", age >= 65 is an equivalent answer.

Question 28

Keep rows for customers living in either New York or New Jersey. Enter only the condition that replaces [?].

ny_nj_customers <- custdata |>
  filter([?])

state_of_res == "New York" | state_of_res == "New Jersey"

The completed filter retains 6,490 rows.

Question 29

Keep rows where is_employed is missing. Enter only the condition that replaces [?].

employment_missing <- custdata |>
  filter([?])

is.na(is_employed)

The completed filter retains 25,774 rows.

Question 30

Keep rows where gas_usage is not missing. Enter only the condition that replaces [?].

gas_usage_known <- custdata |>
  filter([?])

!is.na(gas_usage)

The completed filter retains 71,542 rows.

Question 31

Sort all customer rows by income from highest to lowest. Enter only the code that replaces [?].

highest_income_first <- custdata |>
  arrange([?])

desc(income)

The completed arrange() call puts the largest incomes first.

Question 32

Sort by state_of_res alphabetically and, within each state, by age from oldest to youngest. Enter only the second sorting expression that replaces [?].

state_then_oldest <- custdata |>
  arrange(state_of_res, [?])

desc(age)

The first sorting key, state_of_res, stays alphabetical; desc(age) orders ages within each state from oldest to youngest.

Question 33

Create a one-column data frame containing each distinct state of residence once. Enter only the column name that replaces [?].

states <- custdata |>
  distinct([?])

state_of_res

The result is a one-column data frame with 51 distinct state-of-residence values.

Question 34

Keep only the custid, age, and income columns, in that order. Enter only the column-name arguments that replace [?].

id_age_income <- custdata |>
  select([?])

custid, age, income

select() keeps those three columns in the order listed.

Question 35

The pipeline below keeps female customers aged 65 or older, puts the highest incomes first, and keeps three columns. In the final step, rename income to annual_income. Enter only the function call that replaces [?].

older_women_income <- custdata |>
  filter(sex == "Female", age >= 65) |>
  arrange(desc(income)) |>
  select(custid, age, income) |>
  [?]

rename(annual_income = income)

The full pipeline returns 8,684 rows and the columns custid, age, and annual_income. rename() uses new_name = old_name.

Back to top