Satya Sankeerth
2025-12-04
Pixar films are known for both artistic quality and strong audience reception. This project uses the TidyTuesday Pixar data to examine how film characteristics (such as runtime and release date) relate to public and critic ratings.
By combining information on film metadata (pixar_films) and review scores (public_response), I explore whether longer films tend to receive higher ratings, how ratings have changed over time, and whether critics and audiences agree in their evaluation of Pixar movies.
Is there a relationship between film runtime and ratings (Rotten Tomatoes, Metacritic, Critics Choice)?
Have Pixar film ratings changed over the years (are newer films rated higher or lower)?
How strongly do different rating sources (Rotten Tomatoes, Metacritic, Critics Choice) agree with each other?
Which Pixar films are the overall “top-rated” when combining multiple rating sources?
The data are from TidyTuesday week of 2025-03-11, which in turn uses the open pixarfilms R data package containing multiple Pixar-related tables.
Key CSVs used:
pixar_films.csv – film metadata (title, release date, runtime, MPAA rating)
public_response.csv – public and critic ratings for each film
#Load Packages
## Warning: package 'tidytuesdayR' was built under R version 4.5.2
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
#Load data
## ---- Compiling #TidyTuesday Information for 2025-03-11 ----
## --- There are 2 files available ---
##
##
## ── Downloading files ───────────────────────────────────────────────────────────
##
## 1 of 2: "pixar_films.csv"
## 2 of 2: "public_response.csv"
The Pixar data are described in:
The TidyTuesday readme for 2025-03-11 (Pixar films week). GitHub
The pixarfilms R package documentation on CRAN, which includes a description of each table and a small data dictionary.
Important variables: From pixar_films: number – order of release film – film title release_date – date premiered run_time – runtime in minutes film_rating – MPAA/BBFC rating (e.g., “G”, “PG”) From public_response: film – film title (join key) rotten_tomatoes – Rotten Tomatoes score (0–100) metacritic – Metacritic score (0–100) cinema_score – CinemaScore letter grade (A–F) critics_choice – Critics Choice score (0–100)
Use the tools in R such as str() and summary() to describe the original dataset you imported.
## spc_tbl_ [27 × 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ number : num [1:27] 1 2 3 4 5 6 7 8 9 10 ...
## $ film : chr [1:27] "Toy Story" "A Bug's Life" "Toy Story 2" "Monsters, Inc." ...
## $ release_date: Date[1:27], format: "1995-11-22" "1998-11-25" ...
## $ run_time : num [1:27] 81 95 92 92 100 115 117 111 98 96 ...
## $ film_rating : chr [1:27] "G" "G" "G" "G" ...
## - attr(*, "spec")=
## .. cols(
## .. number = col_double(),
## .. film = col_character(),
## .. release_date = col_date(format = ""),
## .. run_time = col_double(),
## .. film_rating = col_character()
## .. )
## - attr(*, "problems")=<externalptr>
## number film release_date run_time
## Min. : 1.0 Length:27 Min. :1995-11-22 Min. : 81.0
## 1st Qu.: 7.5 Class :character 1st Qu.:2006-12-18 1st Qu.: 95.0
## Median :14.0 Mode :character Median :2013-06-21 Median :100.0
## Mean :14.0 Mean :2012-06-15 Mean :104.8
## 3rd Qu.:20.5 3rd Qu.:2018-12-17 3rd Qu.:106.0
## Max. :27.0 Max. :2023-06-16 Max. :155.0
## NA's :2
## film_rating
## Length:27
## Class :character
## Mode :character
##
##
##
##
## spc_tbl_ [24 × 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ film : chr [1:24] "Toy Story" "A Bug's Life" "Toy Story 2" "Monsters, Inc." ...
## $ rotten_tomatoes: num [1:24] 100 92 100 96 99 97 74 96 95 98 ...
## $ metacritic : num [1:24] 95 77 88 79 90 90 73 96 95 88 ...
## $ cinema_score : chr [1:24] "A" "A" "A+" "A+" ...
## $ critics_choice : num [1:24] NA NA 100 92 97 88 89 91 90 95 ...
## - attr(*, "spec")=
## .. cols(
## .. film = col_character(),
## .. rotten_tomatoes = col_double(),
## .. metacritic = col_double(),
## .. cinema_score = col_character(),
## .. critics_choice = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
## film rotten_tomatoes metacritic cinema_score
## Length:24 Min. : 40.00 Min. :57.00 Length:24
## Class :character 1st Qu.: 84.00 1st Qu.:71.00 Class :character
## Mode :character Median : 96.00 Median :81.00 Mode :character
## Mean : 89.17 Mean :79.96
## 3rd Qu.: 97.50 3rd Qu.:90.00
## Max. :100.00 Max. :96.00
## NA's :1 NA's :1
## critics_choice
## Min. : 66.00
## 1st Qu.: 81.00
## Median : 89.00
## Mean : 87.14
## 3rd Qu.: 93.00
## Max. :100.00
## NA's :3
To analyze ratings together with film metadata, I join the two tables on film:
pixar <- pixar_films |>
inner_join(public_response, by = "film") |>
mutate(
release_year = year(release_date),
avg_score = (rotten_tomatoes + metacritic + critics_choice) / 3
)
str(pixar)## tibble [24 × 11] (S3: tbl_df/tbl/data.frame)
## $ number : num [1:24] 1 2 3 4 5 6 7 8 9 10 ...
## $ film : chr [1:24] "Toy Story" "A Bug's Life" "Toy Story 2" "Monsters, Inc." ...
## $ release_date : Date[1:24], format: "1995-11-22" "1998-11-25" ...
## $ run_time : num [1:24] 81 95 92 92 100 115 117 111 98 96 ...
## $ film_rating : chr [1:24] "G" "G" "G" "G" ...
## $ rotten_tomatoes: num [1:24] 100 92 100 96 99 97 74 96 95 98 ...
## $ metacritic : num [1:24] 95 77 88 79 90 90 73 96 95 88 ...
## $ cinema_score : chr [1:24] "A" "A" "A+" "A+" ...
## $ critics_choice : num [1:24] NA NA 100 92 97 88 89 91 90 95 ...
## $ release_year : num [1:24] 1995 1998 1999 2001 2003 ...
## $ avg_score : num [1:24] NA NA 96 89 95.3 ...
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 54.67 76.00 89.00 85.02 93.67 96.00 3
Cleaning steps: Inner join pixar_films and public_response on film. Create release year and average rating. Handle missing values in scores or runtime. Convert CinemaScore letters to an ordered factor if needed.
pixar_clean <- pixar_films |>
inner_join(public_response, by = "film") |>
mutate(
release_year = year(release_date),
avg_score = (rotten_tomatoes + metacritic + critics_choice) / 3,
cinema_score = factor(
cinema_score,
levels = c("F", "D", "C", "B", "A"),
ordered = TRUE
)
) |>
filter(
!is.na(run_time),
!is.na(rotten_tomatoes),
!is.na(metacritic),
!is.na(critics_choice)
)
summary(select(pixar_clean, run_time, rotten_tomatoes, metacritic, critics_choice, avg_score))## run_time rotten_tomatoes metacritic critics_choice
## Min. : 92.0 Min. : 40.00 Min. :57.00 Min. : 66.00
## 1st Qu.: 96.0 1st Qu.: 80.00 1st Qu.:69.00 1st Qu.: 81.00
## Median :100.0 Median : 96.00 Median :81.00 Median : 89.00
## Mean :101.9 Mean : 88.52 Mean :79.38 Mean : 87.14
## 3rd Qu.:105.0 3rd Qu.: 97.00 3rd Qu.:90.00 3rd Qu.: 93.00
## Max. :118.0 Max. :100.00 Max. :96.00 Max. :100.00
## avg_score
## Min. :54.67
## 1st Qu.:76.00
## Median :89.00
## Mean :85.02
## 3rd Qu.:93.67
## Max. :96.00
Q1 – Runtime vs. ratings
ggplot(pixar_clean,
aes(x = run_time,
y = avg_score)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
labs(
title = "Runtime vs. Average Rating for Pixar Films",
x = "Runtime (minutes)",
y = "Average rating (Rotten Tomatoes, Metacritic, Critics Choice)"
)## `geom_smooth()` using formula = 'y ~ x'
##
## Call:
## lm(formula = avg_score ~ run_time, data = pixar_clean)
##
## Residuals:
## Min 1Q Median 3Q Max
## -29.737 -8.995 4.449 8.593 10.820
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 100.0682 33.6963 2.970 0.00787 **
## run_time -0.1478 0.3299 -0.448 0.65922
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 11.71 on 19 degrees of freedom
## Multiple R-squared: 0.01045, Adjusted R-squared: -0.04163
## F-statistic: 0.2007 on 1 and 19 DF, p-value: 0.6592
##Q2 – Ratings over time
ggplot(pixar_clean,
aes(x = release_year,
y = avg_score)) +
geom_point() +
geom_smooth(method = "loess", se = FALSE) +
labs(
title = "Average Pixar Film Rating Over Time",
x = "Release year",
y = "Average rating"
)## `geom_smooth()` using formula = 'y ~ x'
##Q3 – Agreement between rating sources
ratings_only <- pixar_clean |>
select(rotten_tomatoes, metacritic, critics_choice)
cor(ratings_only, use = "complete.obs")## rotten_tomatoes metacritic critics_choice
## rotten_tomatoes 1.0000000 0.8018090 0.8521907
## metacritic 0.8018090 1.0000000 0.8645541
## critics_choice 0.8521907 0.8645541 1.0000000
##Q4 – Top-rated films
top_pixar <- pixar_clean |>
arrange(desc(avg_score)) |>
select(film, release_year, run_time,
rotten_tomatoes, metacritic, critics_choice, avg_score) |>
slice_head(n = 5)
top_pixar## # A tibble: 5 × 7
## film release_year run_time rotten_tomatoes metacritic critics_choice
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Toy Story 2 1999 92 100 88 100
## 2 Toy Story 3 2010 103 98 92 97
## 3 Finding Nemo 2003 100 99 90 97
## 4 Inside Out 2015 95 98 94 93
## 5 Ratatouille 2007 111 96 96 91
## # ℹ 1 more variable: avg_score <dbl>
ggplot(top_pixar,
aes(x = reorder(film, avg_score),
y = avg_score)) +
geom_col() +
coord_flip() +
labs(
title = "Top 5 Pixar Films by Combined Rating",
x = "Film",
y = "Average rating"
)