| market_segment | bookings | mean_nights |
|---|---|---|
| Online TA | 56477 | 3.57 |
| Offline TA/TO | 24219 | 3.90 |
| Groups | 19811 | 2.99 |
DATA 121 | Friday, September 25, 2026
Open your RStudio project
With a peer | 6 minutes
How many bookings are in each market segment, and what is their mean booked stay length?
Predict the table’s structure, then run the code and inspect the result.
Write code:
Start with the hotel_stays object
Group by market_segment.
Keep both summaries.
Summarize the number of bookings per market segment and the mean total_stay for each market segment.
Save as segment_summary, order by bookings, largest to smallest.
Your result should have eight rows, with these three first:
| market_segment | bookings | mean_nights |
|---|---|---|
| Online TA | 56477 | 3.57 |
| Offline TA/TO | 24219 | 3.90 |
| Groups | 19811 | 2.99 |
The full code and all eight rows are in the reference slides at the end of the slides
What do these columns mean?
Park observations
| Park ID | Access | Condition | Trees | Temp. °C |
|---|---|---|---|---|
| 001 | Public | Good | 12 | 29.4 |
| 002 | Private | Fair | 0 | 31.2 |
| 003 | Public | Poor | NA | NA |
One row represents one park at the time of observation.
Which columns identify or describe categories? Which measure quantities?
Nominal: categories without a meaningful order or ranking.
001, 002, 003.Ordinal: categories with an order, without established equal gaps.
1, 2, 3 preserves order; it does not establish equal distances.Can we say that the change from Poor to Fair necessarily the same as Fair to Good?
| Variable | Possible values | Numerical kind |
|---|---|---|
| Number of trees | 0, 1, 2, … | Discrete |
| Recorded year opened | Whole years | Discrete |
| Temperature | Values along a scale | Continuous |
| Area | Values along a scale | Continuous |
Discrete quantities have separated possible values.
Continuous quantities can take values between recorded measurements.
Note: Rounding a temperature to a whole degree does not turn it into a count.
| Measurement level | What it supports | Example |
|---|---|---|
| Nominal | Same or different category | Public / Private |
| Ordinal | Category and order | Poor < Fair < Good |
| Interval | Order and equal differences | Temperature in °C |
| Ratio | Equal differences and meaningful ratios | Tree count or area |
These describe the meaning of a measurement
They are separate from discrete/continuous and from R’s storage types (num, chr, dbl)
| Celsius | Fahrenheit |
|---|---|
| 20°C | 68°F |
| 40°C | 104°F |
40 / 20 = 2, but 104 / 68 ≈ 1.53.Celsius and Fahrenheit have reference zeros. Ratios of their values do not measure “times as hot.”
| Variable | A useful comparison |
|---|---|
| Tree count | 40 trees is twice 20 trees. |
| Area | 2,000 m² is twice 1,000 m². |
| Calendar year | 2010 is 10 years after 2000. |
| Elapsed time | 20 minutes is twice 10 minutes. |
Zero trees means none counted. Year zero is a calendar reference.
Calendar date and elapsed time support different comparisons.
park_id <- c("001", "002", "003")
condition_code <- c(3, 2, 1) # Good, Fair, Poor
class(park_id) # "character"
class(condition_code) # "numeric"class() helps explain R’s behavior; documentation explains the values.0: a recorded count of no trees.NA: the count is missing.Report what was missing and which observations entered the summary.
Invented observations · one row per building
| ID | Use | Condition | Built | Vacant units | Area (m²) |
|---|---|---|---|---|---|
| 001 | Residential | Good | 1920 | 0 | 240.5 |
| 002 | Mixed use | Poor | 1960 | 4 | 481.0 |
| 003 | Residential | Fair | 2000 | NA | 360.0 |
| 004 | Mixed use | Good | 1985 | 2 | 600.8 |
Built is the recorded calendar year. Vacant units is a count. NA means the count is missing.
With a partner | 4 minutes
For each housing variable:
Then choose one variable. Propose a useful summary and one misleading calculation.
Use the examples in these slides. Record which example helped you decide.
| Variable | Kind | Measurement level |
|---|---|---|
| ID | Categorical identifier | Nominal |
| Use | Categorical | Nominal |
| Condition | Categorical, ordered | Ordinal |
| Built | Numerical, discrete whole years | Interval |
| Vacant units | Numerical, discrete count | Ratio |
| Area | Numerical, continuous | Ratio |
The explanation matters: what does a difference, order, or ratio mean here?
| Question | Variable | Useful starting point |
|---|---|---|
| How many bookings at each hotel? | Hotel category | Counts and a bar chart |
| How far ahead are bookings entered? | Lead time in days | Distribution and a histogram |
| What is the mean booked stay by hotel? | Category + nights | Grouped counts and means |
Save a new Quarto document in activities in your course project.
library(tidyverse)
hotels <- read_csv("data/hotels.csv")
hotel_stays <- hotels |>
mutate(total_nights = stays_in_weekend_nights +
stays_in_week_nights)Run these chunks before the plot examples. If R is running from activities, use ../data/hotels.csv.
… based on the concepts of functions as verbs that manipulate data frames
Functions that manipulate existing data:
select: pick columns by namearrange: reorder rowsslice: pick rows using index(es)filter: pick rows matching criteriadistinct: filter for unique rows… based on the concepts of functions as verbs that manipulate data frames
Functions that generate new data:
mutate: add new variablessummarize: reduce variables to valuesgroup_by: for grouped operationscount: to create frequency tablesdplyr functions expect a data frame as input, produce a data frame as output
[1] "hotel" "is_canceled"
[3] "lead_time" "arrival_date_year"
[5] "arrival_date_month" "arrival_date_week_number"
[7] "arrival_date_day_of_month" "stays_in_weekend_nights"
[9] "stays_in_week_nights" "adults"
[11] "children" "babies"
[13] "meal" "country"
[15] "market_segment" "distribution_channel"
[17] "is_repeated_guest" "previous_cancellations"
[19] "previous_bookings_not_canceled" "reserved_room_type"
[21] "assigned_room_type" "booking_changes"
[23] "deposit_type" "agent"
[25] "company" "days_in_waiting_list"
[27] "customer_type" "adr"
[29] "required_car_parking_spaces" "total_of_special_requests"
[31] "reservation_status" "reservation_status_date"
Rows: 119,390
Columns: 32
$ hotel <chr> "Resort Hotel", "Resort Hotel", "Resort…
$ is_canceled <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, …
$ lead_time <dbl> 342, 737, 7, 13, 14, 14, 0, 9, 85, 75, …
$ arrival_date_year <dbl> 2015, 2015, 2015, 2015, 2015, 2015, 201…
$ arrival_date_month <chr> "July", "July", "July", "July", "July",…
$ arrival_date_week_number <dbl> 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,…
$ arrival_date_day_of_month <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ stays_in_weekend_nights <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ stays_in_week_nights <dbl> 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 4, …
$ adults <dbl> 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, …
$ children <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ babies <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ meal <chr> "BB", "BB", "BB", "BB", "BB", "BB", "BB…
$ country <chr> "PRT", "PRT", "GBR", "GBR", "GBR", "GBR…
$ market_segment <chr> "Direct", "Direct", "Direct", "Corporat…
$ distribution_channel <chr> "Direct", "Direct", "Direct", "Corporat…
$ is_repeated_guest <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ previous_cancellations <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ previous_bookings_not_canceled <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ reserved_room_type <chr> "C", "C", "A", "A", "A", "A", "C", "C",…
$ assigned_room_type <chr> "C", "C", "C", "A", "A", "A", "C", "C",…
$ booking_changes <dbl> 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ deposit_type <chr> "No Deposit", "No Deposit", "No Deposit…
$ agent <chr> "NULL", "NULL", "NULL", "304", "240", "…
$ company <chr> "NULL", "NULL", "NULL", "NULL", "NULL",…
$ days_in_waiting_list <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ customer_type <chr> "Transient", "Transient", "Transient", …
$ adr <dbl> 0.00, 0.00, 75.00, 75.00, 98.00, 98.00,…
$ required_car_parking_spaces <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ total_of_special_requests <dbl> 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 3, …
$ reservation_status <chr> "Check-Out", "Check-Out", "Check-Out", …
$ reservation_status_date <date> 2015-07-01, 2015-07-01, 2015-07-02, 20…
[1] "hotel" "is_canceled"
[3] "lead_time" "arrival_date_year"
[5] "arrival_date_month" "arrival_date_week_number"
[7] "arrival_date_day_of_month" "stays_in_weekend_nights"
[9] "stays_in_week_nights" "adults"
[11] "children" "babies"
[13] "meal" "country"
[15] "market_segment" "distribution_channel"
[17] "is_repeated_guest" "previous_cancellations"
[19] "previous_bookings_not_canceled" "reserved_room_type"
[21] "assigned_room_type" "booking_changes"
[23] "deposit_type" "agent"
[25] "company" "days_in_waiting_list"
[27] "customer_type" "adr"
[29] "required_car_parking_spaces" "total_of_special_requests"
[31] "reservation_status" "reservation_status_date"
[1] "hotel" "is_canceled"
[3] "lead_time" "arrival_date_year"
[5] "arrival_date_month" "arrival_date_week_number"
[7] "arrival_date_day_of_month" "stays_in_weekend_nights"
[9] "stays_in_week_nights" "adults"
[11] "children" "babies"
[13] "meal" "country"
[15] "market_segment" "distribution_channel"
[17] "is_repeated_guest" "previous_cancellations"
[19] "previous_bookings_not_canceled" "reserved_room_type"
[21] "assigned_room_type" "booking_changes"
[23] "deposit_type" "agent"
[25] "company" "days_in_waiting_list"
[27] "customer_type" "adr"
[29] "required_car_parking_spaces" "total_of_special_requests"
[31] "reservation_status" "reservation_status_date"
[33] "little_ones"
In programming, a pipe is a technique for passing information from one process to another.
hotels, and pass it to the select() function,# A tibble: 119,390 × 2
hotel lead_time
<chr> <dbl>
1 Resort Hotel 737
2 Resort Hotel 709
3 City Hotel 629
4 City Hotel 629
5 City Hotel 629
6 City Hotel 629
7 City Hotel 629
8 City Hotel 629
9 City Hotel 629
10 City Hotel 629
# ℹ 119,380 more rows
In programming, a pipe is a technique for passing information from one process to another.
hotels, and pass it to the select() function,hotel and lead_time, # A tibble: 119,390 × 2
hotel lead_time
<chr> <dbl>
1 Resort Hotel 737
2 Resort Hotel 709
3 City Hotel 629
4 City Hotel 629
5 City Hotel 629
6 City Hotel 629
7 City Hotel 629
8 City Hotel 629
9 City Hotel 629
10 City Hotel 629
# ℹ 119,380 more rows
In programming, a pipe is a technique for passing information from one process to another.
hotels, and pass it to the select() function,hotel and lead_time,lead_time in descending order.# A tibble: 119,390 × 2
hotel lead_time
<chr> <dbl>
1 Resort Hotel 737
2 Resort Hotel 709
3 City Hotel 629
4 City Hotel 629
5 City Hotel 629
6 City Hotel 629
7 City Hotel 629
8 City Hotel 629
9 City Hotel 629
10 City Hotel 629
# ℹ 119,380 more rows
This becomes useful when we want to chain together several operations.
filterfilter to select a subset of rows# A tibble: 79,330 × 33
hotel is_canceled lead_time arrival_date_year arrival_date_month
<chr> <dbl> <dbl> <dbl> <chr>
1 City Hotel 0 6 2015 July
2 City Hotel 1 88 2015 July
3 City Hotel 1 65 2015 July
4 City Hotel 1 92 2015 July
5 City Hotel 1 100 2015 July
6 City Hotel 1 79 2015 July
7 City Hotel 0 3 2015 July
8 City Hotel 1 63 2015 July
9 City Hotel 1 62 2015 July
10 City Hotel 1 62 2015 July
# ℹ 79,320 more rows
# ℹ 28 more variables: arrival_date_week_number <dbl>,
# arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
# stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
# meal <chr>, country <chr>, market_segment <chr>,
# distribution_channel <chr>, is_repeated_guest <dbl>,
# previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>, …
| operator | definition | operator | definition |
|---|---|---|---|
< |
less than | x | y |
x OR y |
<= |
less than or equal to | is.na(x) |
test if x is NA |
> |
greater than | !is.na(x) |
test if x is not NA |
>= |
greater than or equal to | x %in% y |
test if x is in y |
== |
exactly equal to | !(x %in% y) |
test if x is not in y |
!= |
not equal to | !x |
not x |
x & y |
x AND y |
filter for many conditions at onceselect to select variables (columns)View only lead_time (number of days between booking and arrival date):
select()hotelslead_timeTip
dplyr functions always expect a data frame and always yield a data frame.
select to keep variables (columns)select to exclude variables# A tibble: 119,390 × 32
hotel is_canceled lead_time arrival_date_year arrival_date_month
<chr> <dbl> <dbl> <dbl> <chr>
1 Resort Hotel 0 342 2015 July
2 Resort Hotel 0 737 2015 July
3 Resort Hotel 0 7 2015 July
4 Resort Hotel 0 13 2015 July
5 Resort Hotel 0 14 2015 July
6 Resort Hotel 0 14 2015 July
7 Resort Hotel 0 0 2015 July
8 Resort Hotel 0 9 2015 July
9 Resort Hotel 1 85 2015 July
10 Resort Hotel 1 75 2015 July
# ℹ 119,380 more rows
# ℹ 27 more variables: arrival_date_week_number <dbl>,
# arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
# stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
# meal <chr>, country <chr>, market_segment <chr>,
# distribution_channel <chr>, is_repeated_guest <dbl>,
# previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>, …
select a range of variables# A tibble: 119,390 × 5
hotel is_canceled lead_time arrival_date_year arrival_date_month
<chr> <dbl> <dbl> <dbl> <chr>
1 Resort Hotel 0 342 2015 July
2 Resort Hotel 0 737 2015 July
3 Resort Hotel 0 7 2015 July
4 Resort Hotel 0 13 2015 July
5 Resort Hotel 0 14 2015 July
6 Resort Hotel 0 14 2015 July
7 Resort Hotel 0 0 2015 July
8 Resort Hotel 0 9 2015 July
9 Resort Hotel 1 85 2015 July
10 Resort Hotel 1 75 2015 July
# ℹ 119,380 more rows
select variables with certain characteristics# A tibble: 119,390 × 4
arrival_date_year arrival_date_month arrival_date_week_number
<dbl> <chr> <dbl>
1 2015 July 27
2 2015 July 27
3 2015 July 27
4 2015 July 27
5 2015 July 27
6 2015 July 27
7 2015 July 27
8 2015 July 27
9 2015 July 27
10 2015 July 27
# ℹ 119,380 more rows
# ℹ 1 more variable: arrival_date_day_of_month <dbl>
select variables with certain characteristics# A tibble: 119,390 × 4
reserved_room_type assigned_room_type deposit_type customer_type
<chr> <chr> <chr> <chr>
1 C C No Deposit Transient
2 C C No Deposit Transient
3 A C No Deposit Transient
4 A A No Deposit Transient
5 A A No Deposit Transient
6 A A No Deposit Transient
7 C C No Deposit Transient
8 C C No Deposit Transient
9 A A No Deposit Transient
10 D D No Deposit Transient
# ℹ 119,380 more rows
starts_with(): Starts with a prefixends_with(): Ends with a suffixcontains(): Contains a literal stringnum_range(): Matches a numerical range like x01, x02, x03one_of(): Matches variable names in a character vectoreverything(): Matches all variableslast_col(): Select last variable, possibly with an offsetmatches(): Matches a regular expression (a sequence of symbols/characters expressing a string/pattern to be searched for within text)See help for any of these functions for more info, e.g. ?everything.
View only the hotel type and lead_time:
What if we wanted to select these columns, and then arrange the data in descending order of lead time?
Select:
Select, then arrange:
# A tibble: 119,390 × 2
hotel lead_time
<chr> <dbl>
1 Resort Hotel 737
2 Resort Hotel 709
3 City Hotel 629
4 City Hotel 629
5 City Hotel 629
6 City Hotel 629
7 City Hotel 629
8 City Hotel 629
9 City Hotel 629
10 City Hotel 629
# ℹ 119,380 more rows
arrange in ascending / descending orderslice for certain row numbers# A tibble: 5 × 33
hotel is_canceled lead_time arrival_date_year arrival_date_month
<chr> <dbl> <dbl> <dbl> <chr>
1 Resort Hotel 0 342 2015 July
2 Resort Hotel 0 737 2015 July
3 Resort Hotel 0 7 2015 July
4 Resort Hotel 0 13 2015 July
5 Resort Hotel 0 14 2015 July
# ℹ 28 more variables: arrival_date_week_number <dbl>,
# arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
# stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
# meal <chr>, country <chr>, market_segment <chr>,
# distribution_channel <chr>, is_repeated_guest <dbl>,
# previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>,
# reserved_room_type <chr>, assigned_room_type <chr>, …
Tip
In R, you can use the # for adding comments to your code. Any text following # will be printed as is, and won’t be run as R code. This is useful for leaving comments in your code and for temporarily disabling certain lines of code while debugging.
hotels |>
# slice the first five rows # this line is a comment
#select(hotel) |> # this one doesn't run
slice(1:5) # this line runs# A tibble: 5 × 33
hotel is_canceled lead_time arrival_date_year arrival_date_month
<chr> <dbl> <dbl> <dbl> <chr>
1 Resort Hotel 0 342 2015 July
2 Resort Hotel 0 737 2015 July
3 Resort Hotel 0 7 2015 July
4 Resort Hotel 0 13 2015 July
5 Resort Hotel 0 14 2015 July
# ℹ 28 more variables: arrival_date_week_number <dbl>,
# arrival_date_day_of_month <dbl>, stays_in_weekend_nights <dbl>,
# stays_in_week_nights <dbl>, adults <dbl>, children <dbl>, babies <dbl>,
# meal <chr>, country <chr>, market_segment <chr>,
# distribution_channel <chr>, is_repeated_guest <dbl>,
# previous_cancellations <dbl>, previous_bookings_not_canceled <dbl>,
# reserved_room_type <chr>, assigned_room_type <chr>, …
distinct and countdistinct to filter for unique rowsAnd arrange to order alphabetically
# A tibble: 14 × 2
hotel market_segment
<chr> <chr>
1 City Hotel Aviation
2 City Hotel Complementary
3 City Hotel Corporate
4 City Hotel Direct
5 City Hotel Groups
6 City Hotel Offline TA/TO
7 City Hotel Online TA
8 City Hotel Undefined
9 Resort Hotel Complementary
10 Resort Hotel Corporate
11 Resort Hotel Direct
12 Resort Hotel Groups
13 Resort Hotel Offline TA/TO
14 Resort Hotel Online TA
count to create frequency tablescount and arrangecount for multiple variables# A tibble: 14 × 3
hotel market_segment n
<chr> <chr> <int>
1 City Hotel Aviation 237
2 City Hotel Complementary 542
3 City Hotel Corporate 2986
4 City Hotel Direct 6093
5 City Hotel Groups 13975
6 City Hotel Offline TA/TO 16747
7 City Hotel Online TA 38748
8 City Hotel Undefined 2
9 Resort Hotel Complementary 201
10 Resort Hotel Corporate 2309
11 Resort Hotel Direct 6513
12 Resort Hotel Groups 5836
13 Resort Hotel Offline TA/TO 7472
14 Resort Hotel Online TA 17729
count# A tibble: 14 × 3
hotel market_segment n
<chr> <chr> <int>
1 City Hotel Aviation 237
2 City Hotel Complementary 542
3 City Hotel Corporate 2986
4 City Hotel Direct 6093
5 City Hotel Groups 13975
6 City Hotel Offline TA/TO 16747
7 City Hotel Online TA 38748
8 City Hotel Undefined 2
9 Resort Hotel Complementary 201
10 Resort Hotel Corporate 2309
11 Resort Hotel Direct 6513
12 Resort Hotel Groups 5836
13 Resort Hotel Offline TA/TO 7472
14 Resort Hotel Online TA 17729
# A tibble: 14 × 3
market_segment hotel n
<chr> <chr> <int>
1 Aviation City Hotel 237
2 Complementary City Hotel 542
3 Complementary Resort Hotel 201
4 Corporate City Hotel 2986
5 Corporate Resort Hotel 2309
6 Direct City Hotel 6093
7 Direct Resort Hotel 6513
8 Groups City Hotel 13975
9 Groups Resort Hotel 5836
10 Offline TA/TO City Hotel 16747
11 Offline TA/TO Resort Hotel 7472
12 Online TA City Hotel 38748
13 Online TA Resort Hotel 17729
14 Undefined City Hotel 2
What is happening in the following chunk?
hotels |>
mutate(little_ones = children + babies) |>
count(hotel, little_ones) |>
mutate(prop = n / sum(n))# A tibble: 12 × 4
hotel little_ones n prop
<chr> <dbl> <int> <dbl>
1 City Hotel 0 73923 0.619
2 City Hotel 1 3263 0.0273
3 City Hotel 2 2056 0.0172
4 City Hotel 3 82 0.000687
5 City Hotel 9 1 0.00000838
6 City Hotel 10 1 0.00000838
7 City Hotel NA 4 0.0000335
8 Resort Hotel 0 36131 0.303
9 Resort Hotel 1 2183 0.0183
10 Resort Hotel 2 1716 0.0144
11 Resort Hotel 3 29 0.000243
12 Resort Hotel 10 1 0.00000838
summarize and group_bysummarize for summary stats# A tibble: 1 × 1
mean_adr
<dbl>
1 102.
Tip
summarize() changes entirely the kind of info we get from the data frame. It collapses rows down to a single summary statistic, and removes all columns that are irrelevant to the calculation.
group_by for grouped operationsThe following two give the same result, so count is simply short for group_by, then determine frequencies
summarize can be used for multiple summary statistics
| hotel |
|---|
| City Hotel |
| Resort Hotel |
distinct() keeps one row for each distinct combination of the selected columns. This result lists hotel categories; it does not count their bookings.
These return the same frequency table:
| hotel | n |
|---|---|
| City Hotel | 79,330 |
| Resort Hotel | 40,060 |
count() is a shortcut for grouped row counts. Use count(hotel, sort = TRUE) to order by frequency, largest first.
Open R4DS 1.2.1. Use section 1.1.1 to check the package setup.
| In the reading | In our hotel work |
|---|---|
penguins: one row per penguin |
hotel_stays: one row per booking |
species: a category |
hotel: a category |
body_mass_g: mass in grams |
total_nights: booked nights |
Before using a plot example, identify the input table, the meaning of a row, and the variables and their units.
Where does the chapter load the packages and introduce the data?
R4DS 1.2–1.3: data, mappings, then a geometry. Here is that structure using the hotel table.
ggplot(hotel_stays)
ggplot(hotel_stays, aes(x = hotel))
ggplot(hotel_stays, aes(x = hotel)) +
geom_bar()Read and predict: what does each version add?
Which part supplies the rows? Which maps a column to an axis? Which draws marks?
R4DS 1.4.1: use the plot structure to show counts for a category.
R4DS 1.4.2: keep the plot structure; change the variable and geometry.
binwidth = 200: count observations in intervals 200 grams wide.hotel_stays, lead_time, and a width in days.Predict: what will one bar count after those changes?
The first interval is 0 ≤ lead time < 30 days. Each bar counts bookings.
With a partner | 8 minutes
Question: How are booked stay lengths distributed?
total_nights.binwidth = 1 and boundary = -0.5.3; keep the same boundary.Keep closed = "left". Update the x-axis label to “Booked nights.” Record your source, your changes, and one check.
An average and a histogram answer different parts of the question.
hotel_stays |>
summarize(
shortest = min(total_nights),
mean_nights = mean(total_nights),
median_nights = median(total_nights),
longest = max(total_nights)
)These data: minimum 0, mean about 3.43, median 3, maximum 69 nights.
median() gives the middle value after ordering; min() and max() give the extremes. See IDS 4.4.2.
hotels |> select(hotel, starts_with("arrival"))
hotels |> select(!agent)
hotels |> filter(hotel == "City Hotel", lead_time <= 7)
hotels |> slice(1:5)
hotels |> arrange(desc(lead_time)) |> slice_head(n = 5)filter() must all hold.slice() chooses row positions; their meaning depends on the order.# begins a code comment; R does not run the rest of that line.
segment_summary <- hotel_stays |>
group_by(market_segment) |>
summarize(
bookings = n(),
mean_nights = mean(total_nights)
) |>
arrange(desc(bookings))
segment_summaryChanged: grouping variable, output name, and ordering.
Kept: the input table, row count, and mean calculation.
Extension: Add filter(hotel == "Resort Hotel") |> before group_by().
| market_segment | bookings | mean_nights |
|---|---|---|
| Online TA | 56477 | 3.57 |
| Offline TA/TO | 24219 | 3.90 |
| Groups | 19811 | 2.99 |
| Direct | 12606 | 3.21 |
| Corporate | 5295 | 2.09 |
| Complementary | 743 | 1.65 |
| Aviation | 237 | 3.61 |
| Undefined | 2 | 1.50 |
Check: the counts total 119,390 bookings. A mean based on two bookings deserves different scrutiny from one based on thousands.
mpg adaptation
Check: seven rows; group counts total 234 configurations.
The compact group has 47 configurations and a mean of about 28.30 highway mpg.
These are selected vehicle configurations from 1999 and 2008, not all cars or sales counts.
| What you notice | What to check |
|---|---|
| “Object not found” | Did an earlier chunk create the table? Does that column exist? |
%>% instead of |> |
Both pass the table onward in the simple pipelines used here. |
summarise() instead of summarize() |
These are two spellings of the same dplyr function. |
A mean returns NA |
Inspect missing values before deciding to exclude them. |
Record what was excluded. Removing missing values does not recover the missing information.
R4DS 3.5.3: keep the original row with the largest value in each group.
R4DS 3.5.4: summarize each observed combination of two variables.
slice_max() retains booking rows. This summarize() produces group counts. .groups = "drop" removes grouping from the resulting summary.
hotels supplies the booking rows.aes(x = hotel) puts hotel categories on the horizontal axis.geom_bar() counts the rows in each category.+ adds a plot layer; labs() supplies readable labels.Which part would you change to use a different table?
With a partner · 4 minutes
First, use Friday’s tools to keep bookings for at least seven nights:
Copy the first chart. Change only the input table to long_stays.
aes(x = hotel) and geom_bar().Which chart supports “City has more bookings”? Which supports “Resort has longer stays on average”?
Compare the axis labels and units before comparing bar heights across charts.
ggplot(hotel_stays, aes(x = total_nights)) +
geom_histogram(
binwidth = 1,
boundary = -0.5,
closed = "left"
) +
labs(x = "Booked nights", y = "Number of bookings")For the second version, change only binwidth = 1 to binwidth = 3.
With width 3, the first interval is −0.5 ≤ nights < 2.5: it contains the recorded values 0, 1, and 2.
condition <- factor(
c("Good", "Fair", "Poor"),
levels = c("Poor", "Fair", "Good"),
ordered = TRUE
)
class(condition) # "ordered" "factor"
levels(condition)The levels record category order. They do not establish equal gaps.
glimpse(hotel_stays)
class(hotel_stays$hotel) # "character"
class(hotel_stays$total_nights) # "numeric"
hotel_stays |>
summarize(
bookings = n(),
missing_nights = sum(is.na(total_nights))
)These data have 119,390 bookings and no missing total_nights values. Check again when the input changes.
Invented yearly booking totals:
| Hotel | 2024 | 2025 |
|---|---|---|
| City | 12 | 15 |
| Resort | 8 | 10 |
For an analysis across years, put year values in a year column:
| Hotel | year | bookings |
|---|---|---|
| City | 2024 | 12 |
| City | 2025 | 15 |
| Resort | 2024 | 8 |
| Resort | 2025 | 10 |
Here one row is a hotel-year summary. tidyr::pivot_longer() can make this change.
hotel_stays |>
count(hotel, market_segment, sort = TRUE)
hotel_stays |>
distinct(hotel, market_segment)Both results have one row per observed hotel–segment combination. Only count() adds the number of booking rows in each combination.
Changing the order of the variables changes column/default sorting order, not the count for a given combination.
mpg data documentationHotel results were calculated from the same hotels.csv used in the Week 3 lab.