library(tidyverse)
library(dplyr)
library(xts)
library(tsibble)
tuesdata <- tidytuesdayR::tt_load(2025, week = 34)
billboard <- tuesdata$billboard
topics <- tuesdata$topics
head(billboard)
## # A tibble: 6 × 105
## song artist date weeks_at_number_one non_consecutive rating_1
## <chr> <chr> <dttm> <dbl> <dbl> <dbl>
## 1 Poor … Ricky… 1958-08-04 00:00:00 2 0 4
## 2 Nel B… Domen… 1958-08-18 00:00:00 5 1 7
## 3 Littl… The E… 1958-08-25 00:00:00 1 0 5
## 4 It's … Tommy… 1958-09-29 00:00:00 6 0 3
## 5 It's … Conwa… 1958-11-10 00:00:00 2 1 7
## 6 Tom D… The K… 1958-11-17 00:00:00 1 0 5
## # ℹ 99 more variables: rating_2 <dbl>, rating_3 <dbl>, overall_rating <dbl>,
## # divisiveness <dbl>, label <chr>, parent_label <chr>, cdr_genre <chr>,
## # cdr_style <chr>, discogs_genre <chr>, discogs_style <chr>,
## # artist_structure <dbl>, featured_artists <chr>,
## # multiple_lead_vocalists <dbl>, group_named_after_non_lead_singer <dbl>,
## # talent_contestant <chr>, posthumous <dbl>, artist_place_of_origin <chr>,
## # front_person_age <dbl>, artist_male <dbl>, artist_white <dbl>, …
head(topics)
## # A tibble: 6 × 1
## lyrical_topics
## <chr>
## 1 Addiction
## 2 Anger
## 3 Appreciation
## 4 Badassery
## 5 Bad Behavior
## 6 Bad Relationships
Column of Data that Encodes Time:
class(billboard$date)
## [1] "POSIXct" "POSIXt"
billboard <- billboard |>
dplyr::mutate(date = as.Date(date))
class(billboard$date)
## [1] "Date"
Column of Data to Analyze Over Time - Response-like Variable of Interest:
tsibble Object Made up of
new_date &
overall_ratingbillboard_ts <- billboard |>
select(date, overall_rating) |>
as_tibble(index = date)
library(ggplot2)
billboard_ts |>
ggplot(aes(x = date, y = overall_rating)) +
geom_point(alpha = 0.6) +
geom_line(alpha = 0.4) +
labs(
title = "Overall Rating for Billboard Hot 100 #1s Over Time",
x = "Year",
y = "Overall Rating"
)
Based on the full range plot above (1958-2025), we can see that the ratings seem to fluctuate throughout the period as a whole where a majority of the ratings sit roughly between 5-8 and don’t have a common trend from start to finish. There are the occasional very low/very high outliers, but there’s no exact longer term movement either up or down. Lastly, the density of the points seems pretty consistent from start to finish, but the modern era (2000-2025) contains more drastic jumps between songs which could mean modern music is more diverse, or judging has changed a bit in this era specifically compared to the other eras.
billboard_ts |>
filter(date < as.Date("1980-01-01")) |>
ggplot(aes(date, overall_rating)) +
geom_point(alpha = 0.7) +
geom_line(alpha = 0.4) +
labs(
title = "Overall Rating (1958–1980)",
x = "Year",
y = "Overall Rating"
)
The ratings for this earlier period of the dataset (1958-1980) seem consistent and clustered, where a majority of songs sit between an overall rating of 5-7.5, of course with the occasional song reaching above or falling below that range. There also seems to be fewer drastic jumps or falls compared to the previous plot, which could just mean judging in this era was more consistent overall, and the smoother pattern could mean this was an era where the judges’ criteria for a #1 song was more consistent as well.
billboard_ts |>
filter(date >= as.Date("1980-01-01"),
date < as.Date("2000-01-01")) |>
ggplot(aes(date, overall_rating)) +
geom_point(alpha = 0.7) +
geom_line(alpha = 0.4) +
labs(
title = "Overall Rating (1980–2000)",
x = "Year",
y = "Overall Rating"
)
This middle era window (1980-2000) definitely shows a more noticeable spread in the overall ratings of songs. Many songs still fall in the 5-7.5 rating range, but there are many outliers, both high and low, compared to the previous era. These drastic changes may be caused due to large shifts in genre and what was deemed as “popular” music in this era, for example, the come up of hip-hip, R&B, or even alternative, and may have thrown judges for a loop, with either positive or negative reactions. This pattern could suggest the introduction of many new styles/genres of music, and judgement could be thrown off a bit as judges didn’t know how to actually score these new genres compared to the previously known and familiar genres.
billboard_ts |>
filter(date >= as.Date("2000-01-01")) |>
ggplot(aes(date, overall_rating)) +
geom_point(alpha = 0.7) +
geom_line(alpha = 0.4) +
labs(
title = "Overall Rating (2000–2025)",
x = "Year",
y = "Overall Rating"
)
In this modern era plot (2000-2025), we can see that ratings swing dramatically from song to song with many songs reaching a higher range of overall ratings, as well as falling to the lower range of overall ratings a bit more frequently compared to the previous eras. The jumps in this modern era are far greater than the jumps in any of the previous eras shown above. This may suggest that there are rapid changes in production trends overall, as well as how the introduction of music streaming services has changed the game as personal music selection and tracking has grown immensely. Again, similar to the previous middle era window, judgement criteria may have been thrown for a loop once again given these technological changes. Of all the windows, this modern era is definitely the least stable ratings-wise.
model_full <- lm(overall_rating ~ date, data = billboard_ts)
summary(model_full)
##
## Call:
## lm(formula = overall_rating ~ date, data = billboard_ts)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5.3409 -1.2132 0.0537 1.2838 4.1312
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.268e+00 7.351e-02 85.274 < 2e-16 ***
## date -2.108e-05 7.814e-06 -2.698 0.00707 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.812 on 1175 degrees of freedom
## Multiple R-squared: 0.006158, Adjusted R-squared: 0.005312
## F-statistic: 7.281 on 1 and 1175 DF, p-value: 0.007071
early <- billboard_ts |> filter(date < as.Date("1980-01-01"))
model_early <- lm(overall_rating ~ date, data = early)
summary(model_early)
##
## Call:
## lm(formula = overall_rating ~ date, data = early)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5.4357 -1.4137 0.2372 1.5836 3.6041
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.416e+00 9.400e-02 68.255 <2e-16 ***
## date -5.686e-06 4.217e-05 -0.135 0.893
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.057 on 477 degrees of freedom
## Multiple R-squared: 3.81e-05, Adjusted R-squared: -0.002058
## F-statistic: 0.01817 on 1 and 477 DF, p-value: 0.8928
middle <- billboard_ts |>
filter(date >= as.Date("1980-01-01"), date < as.Date("2000-01-01"))
model_middle <- lm(overall_rating ~ date, data = middle)
summary(model_middle)
##
## Call:
## lm(formula = overall_rating ~ date, data = middle)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.5435 -1.1037 0.0328 1.0039 4.2205
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.473e+00 3.180e-01 17.214 <2e-16 ***
## date 4.761e-05 4.426e-05 1.076 0.283
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.571 on 369 degrees of freedom
## Multiple R-squared: 0.003127, Adjusted R-squared: 0.0004255
## F-statistic: 1.158 on 1 and 369 DF, p-value: 0.2827
modern <- billboard_ts |> filter(date >= as.Date("2000-01-01"))
model_modern <- lm(overall_rating ~ date, data = modern)
summary(model_modern)
##
## Call:
## lm(formula = overall_rating ~ date, data = modern)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.1255 -1.1417 0.0773 1.0752 4.0455
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.691e+00 5.268e-01 12.701 <2e-16 ***
## date -3.885e-05 3.314e-05 -1.172 0.242
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.634 on 325 degrees of freedom
## Multiple R-squared: 0.004211, Adjusted R-squared: 0.001147
## F-statistic: 1.374 on 1 and 325 DF, p-value: 0.2419
Above, we’ve utilized linear regression for the overall dataset (1958-2025), as well as broken it up into subsets like we did for the plots, with the early era (1958-1980), middle era (1980-2000), and modern era (2000-2025). After doing this, we can see that all four regression models tell the same story in the sense that there is no meaningful trend in overall rating over time, whether that be the dataset as a whole, or broken up into the specified eras. Across the whole period from 1958 to 2025, the slope is negative, but is so small that it essentially doesn’t effect rating score, and time only roughly explains 0.6% of the variation. The early era provides a slope that is near zero and isn’t statistically significant, suggesting that the ratings in the early era aren’t consistent. The middle era has a slightly positive slope, but like the full dataset regression slope, the effect is extremely small and insignificant, where time again only explains part of a percentage of the variation. The modern era shows the most volatility in the dataset like we saw previously in the plots, but the slope is still insignificant with the R2 near zero as well. All together, the outputs show that the overall ratings fluctuate from song to song as we know, but they don’t increase nor decrease over time.
billboard_ts |>
ggplot(aes(x = date, y = overall_rating)) +
geom_point(alpha = 0.4) +
geom_smooth(method = "loess", span = 0.2, se = FALSE, color = "blue", linewidth = 1) +
labs(
title = "Smoothed Trend of Overall Ratings Over Time",
x = "Year",
y = "Overall Rating"
)
## `geom_smooth()` using formula = 'y ~ x'
ratings_ts <- ts(billboard_ts$overall_rating)
pacf(ratings_ts, main = "PACF of Overall Ratings")
Utilizing LOESS for smoothing and PACF for illustration of seasonality, both implementations show little to no seasonality for overall rating for the Billboard Hot 100 #1 songs dataset. LOESS shows simply only long-term fluctuations that don’t repeat at consistent intervals, so true seasons aren’t exactly present in the dataset in this instance. The PACF plot also supports this as there are no significant partial autocorrelations at any of the lag points, and there is no consistent repeating pattern. With these results, it is clear that overall ratings behave more like noise rather than a seasonal time series, which in hindsight makes sense as the #1 songs don’t necessarily occur at fixed time intervals.