Want to share your content on R bloggers? click here if you have a blog, or here if you don’t.
I’m an anti-streaming music fan and instead stubbornly maintain a large music collection. This time of year, streamers receive an overview of their listens in a “packaged” report. Not wanting to miss this, I started creating my own annual review using R!
If you want to see a selection of my favorite albums of 2025, jump here. If you’re here for the R coding, read on!
I am aware that the audience for this post is small. We non-streamers are a dying breed, but also the number of people who took an XML snapshot of their iTunes/Music library to run this code last year might be zero.
To determine the number of plays in 2025, we need a snapshot of the library that is one year old. It’s then a matter of loading both into R, subtracting the number of songs played from last year’s snapshot from the current one, and then deleting all the songs with a difference of 0.
library(XML)
library(dplyr)
library(ggplot2)
# file paths for last year and this year XML files
lastyear <- "Data/Library_20241210.xml"
thisyear <- "Data/Library_20251203.xml"
# function to read xml file and convert to data frame
read_iTunesXML <- function(path) {
df <- plyr::ldply(lapply(readKeyValueDB(path)$Tracks, data.frame))
df$Play.Count <- replace(df$Play.Count,is.na(df$Play.Count),0)
return(df)
}
# read in the music libraries
library_thisyear <- read_iTunesXML(thisyear)
library_lastyear <- read_iTunesXML(lastyear)
# merge the two libraries to get the correct play counts for this year
# we only need Persistent.ID and Play.Count from last year
sublibrary_lastyear <- library_lastyear[, c("Persistent.ID", "Play.Count")]
# merge
music_library <- merge(library_thisyear, sublibrary_lastyear, by = "Persistent.ID",
all.x = TRUE, sort = FALSE)
music_library$Play.Count.x <- replace(music_library$Play.Count.x,is.na(music_library$Play.Count.x),0)
music_library$Play.Count.y <- replace(music_library$Play.Count.y,is.na(music_library$Play.Count.y),0)
music_library$Play.Count <- music_library$Play.Count.x - music_library$Play.Count.y
# now get the tracks that were listened to in the last year
music_library <- music_library[music_library$Play.Count > 0, ]
# keep relevant columns only
music_library <- music_library %>%
select(Name, Artist, Album, Genre, Year,
Play.Count, Total.Time, Bit.Rate, Date.Added)
So now we have a data frame called music_library with the number of times each song has been played (in the past year). So let’s look at some basic facts about what was played.
## some basic facts about the library # number of tracks listened to nrow(music_library) # total number of different artists listened to length(unique(music_library$Artist)) # total play count sum(music_library$Play.Count) # total time that music has been played, in days (sum(music_library$Total.Time * music_library$Play.Count) / 1000) / (60 * 60 * 24)
This gave me:
> # number of tracks listened to > nrow(music_library) [1] 22785 > # total number of different artists listened to > length(unique(music_library$Artist)) [1] 2611 > # total play count > sum(music_library$Play.Count) [1] 26423 > # total time that music has been played, in days > (sum(music_library$Total.Time * music_library$Play.Count) / 1000) / (60 * 60 * 24) [1] 76.9794
Damn! 77 days of listening to music this past year! That’s >20% of my life listening to music…
# histogram of Years
music_library %>%
ggplot(aes(x = Year)) +
geom_histogram(binwidth = 1) +
lims(x = c(1950,2026)) +
labs(x = "Year", y = "Tracks") +
theme_classic()
ggsave("Output/Plots/yearHistogram.png", width = 6, height = 4, units = "in", dpi = 300, bg = "white")
# same thing for library_this year
library_thisyear %>%
ggplot(aes(x = Year)) +
geom_histogram(binwidth = 1) +
lims(x = c(1950,2026)) +
labs(x = "Year", y = "Tracks") +
theme_classic()
ggsave("Output/Plots/yearHistogram_thisyear.png", width = 6, height = 4, units = "in", dpi = 300, bg = "white")

This past year I’ve been listening to mostly new music (released in 2024 and 2025), with a few songs from previous years. Compared to the library, the preference for new songs played this year is quite high.
So what were the most played artists, albums and genres (before we get to the songs)?
# find the most played artists, albums, and genres
artist <- music_library %>%
group_by(Artist) %>%
summarise(Total_Plays = sum(Play.Count),
n = n(),
Mean_Plays = mean(Play.Count),
Median_Plays = median(Play.Count),
.groups = "keep") %>%
filter(n > 5) %>%
arrange(desc(Mean_Plays))
album <- music_library %>%
group_by(Artist, Album) %>%
summarise(Total_Plays = sum(Play.Count),
n = n(),
Mean_Plays = mean(Play.Count),
Median_Plays = median(Play.Count),
Year = median(Year),
.groups = "keep") %>%
filter(n > 2) %>%
arrange(desc(Mean_Plays))
genre <- music_library %>%
group_by(Genre) %>%
summarise(Total_Plays = sum(Play.Count),
n = n(),
Mean_Plays = mean(Play.Count),
Median_Plays = median(Play.Count),
.groups = "keep") %>%
filter(n > 15) %>%
arrange(desc(Mean_Plays))
We now have three data frames, one for each thing, and I’m sorting by average counts, rather than total counts. This is because the number of tracks is different for each artist, album or genre. Everything is played once when I added it to the library, so if I add 3000 indie tracks and they have a total of 3000 plays, this should be ranked lower than the 300 reggae tracks that have a total of 600 plays.
> head(artist) # A tibble: 6 × 5 # Groups: Artist [6] Artist Total_Plays n Mean_Plays Median_Plays 1 200 Stab Wounds 96 9 10.7 12 2 EYES 97 10 9.7 9 3 Miynt 67 11 6.09 6 4 Bnny 145 25 5.8 5 5 Turnstile 166 29 5.72 4 6 Polygon Window 77 14 5.5 5.5 > head(album) # A tibble: 6 × 7 # Groups: Artist, Album [6] Artist Album Total_Plays n Mean_Plays Median_Plays Year 1 200 Stab Wounds Manual Manic Procedures 96 9 10.7 12 2024 2 EYES SPINNER 97 10 9.7 9 2025 3 Turnstile NEVER ENOUGH 119 14 8.5 9.5 2025 4 Grouper Dragging a Dead Deer Up a Hill 96 12 8 8 2007 5 Bnny Everything 88 14 6.29 5 2021 6 Miynt Rain Money Dogs 67 11 6.09 6 2025 > head(genre) # A tibble: 6 × 5 # Groups: Genre [6] Genre Total_Plays n Mean_Plays Median_Plays 1 Afrobeat 36 16 2.25 1 2 Hard rock 39 23 1.70 2 3 Dream Pop 628 381 1.65 1 4 Ambient 638 397 1.61 1 5 Reggae 122 76 1.61 1 6 Grunge 134 86 1.56 1
Finally, let’s look at the tracks. We can easily sort the music_library dataframe by play count, but (now that we’ve done this) it’s a bit boring because I’m an album listener. The songs in the top ten mainly come from my most played album. So let’s just take the best played song per artist/album to make it more interesting.
# order music_library by Play.Count # take the first occurrence of artist album combination music_library_unique <- music_library %>% arrange(desc(Play.Count)) %>% distinct(Artist, Album, .keep_all = TRUE)
and this gives us:
> # echo the top 10 most played tracks to the console
> music_library_unique %>%
+ select(Name, Artist, Album, Year, Play.Count) %>%
+ head(10)
Name Artist Album Year Play.Count
1 I'll See You Around Silver Sun Neo Wave 1998 21
2 Hands of Eternity 200 Stab Wounds Manual Manic Procedures 2024 16
3 Moving Day For the Overton Window EYES SPINNER 2025 16
4 Stay Hated BENCHPRESS Stay Hated 2012 13
5 August Bnny Everything 2021 12
6 SUNSHOWER Turnstile NEVER ENOUGH 2025 10
7 Orion Mastodon Medium Rarities 2020 9
8 Wind and Snow Grouper Dragging a Dead Deer Up a Hill 2007 8
9 blazing helen island last liasse 2024 8
10 Sudden Storm Ezra Furman Goodbye Small Head 2025 8
So there you have it. I listened to Silver Sun’s I’ll See You Around 21 times. It’s because I tend to play this before I do a run (along with Stay Hated or lately Moving Day For the Overton Window). Highly recommended!
Hopefully this has given you some ideas on how to make your own “wrapped” with R.
Albums from 2025
I enjoy putting together my albums of the year. No idea if readers will find it useful, but I’ve benefited from album recommendations, so I’d like to pass them along. Additionally, I’d like to give a shoutout and link to the artists whose work I enjoyed this year. If you like something here, consider purchasing some merchandise or a release from one of the artists! Previous selections can be found here (2024, 2023, 2021).




















SPINNER – EYES
Genre: hardcore [link]
Muscular hardcore from Denmark. I’ll honestly admit that I only looked into this after seeing it on a list and liking the cover and band logo. It turned out to be one of my favorite albums released this year.
NEVER ENOUGH – Tourniquet
Genre: hardcore
I was looking forward to this release from Turnstile and it did not disappoint. They played a great set at Glastonbury and I enjoyed their Tiny Desk Concert. I even checked out the visual album for this record. The addition of Meg Mills as a second guitar crunching into the mix has improved their sound quite a bit.
Rain Money Dogs – Miynt
Genre: lo-fi [link]
Bedroom doll from Sweden. Miynt is the alias of Fredrika Ribbing, who released this beautiful retro album this year (where retro is early 2000s and I feel old).
Shrunken Elvis – Shrunken Elvis
Genre: post rock [link]
An album of instrumental music with pure, simple guitar vibes. I read about this on the wonderful Tonearm website via Mastodon.
Goodbye Little Head – Ezra Furman
Genre: indie rock [link]
The opening track of this album, Grand Mal, is breathtakingly good. The vocals are pure indie rock, but are formulated as a rap song with lyrics that focus on the experience with epilepsy. The album is full of fragile tunes that are varied in style.
Phonetics on and on – Horse Girl
Genre: indie rock [link]
It seemed like I had this album on pre-order for months, so much so that I forgot about it when it finally came out. The album reminds me of Cub’s Betti-Cola with basic instrumentation and simple melodies.
Public Works and Utilities – Warrington-Runcorn New Town Development Plan
Genre: ambient, electronic [link]
I love the whole concept of WRNTDP for reasons I find difficult to describe. He was playing locally and I saw his performance. It was mainly songs from this album, which are more dance oriented than the previous releases which were more ambient in style.
Bugland – No joy
Genre: shoegaze [link]
I’ve been a big fan of Montreal’s No Joy since Wait To Pleasure, a modern shoegaze classic. This album is more psychedelic rock in style and again I saw her playing locally. She played a great (and very loud) set, one of the highlights of the year.
More – Pulp
Genre: rock
I wasn’t sure if I should include this. The album was a bit patchy, but in some places Pulp was back at its best. Spike Island is a great song. I saw them play in Birmingham and they were fantastic. I admired the fact that they actually got back together and released new material rather than just cashing in and re-releasing their old stuff (of course they did that too), but I appreciated what they did this year – 30 years after I saw them play the main stage at Glastonbury.
The bad fire – Mogwai
Genre: post rock [link]
I love what Mogwai does, even though they keep doing it record after record.
Touch – Turtle
Genre: post rock [link]
Another album that I had on pre-order for a long time. The band has gone through several line-up and sound changes, but in some places, like Axial Seamount, they sounded like the Tortoise of Millions Now Living Will Never Die.
McCartney, you’ll be fine – UNIVERSITY
Genre: noise rock [link]
I had to try out this band because they are from my hometown. I loved this record, which is a bit punk, almost screamo. The man on drums is a powerhouse. They released an EP later that year called YES, which features some of the most unhinged drumming I’ve heard in a long time. Just the song titles tell me that these are a bunch of friends having a good time and not taking themselves too seriously. I got major nostalgia vibes from this record, a fact that would horrify the youngsters in the band I’m sure!
Until the morning – Brian D’Addario
Genre: power pop [link]
Without a new album from The Lemon Twigs, I made do with this release from Brian D’Addario. It’s in the soft rock style. I’m amazed at how mature the songwriting is and the great retro production. Something for the music nerds among us.
Dim Probs – Norse Rhys
Genre: lo-fi [link]
There are several Super Furry Animals related releases on my list. The first is this album of acoustic sweetness from Gruff Rhys. I always enjoy his records, but this is his best in a while.
Under Strawberry Moons – Gulp
Genre: psychedelic folk [link]
An album full of spread folk with dreamy vocals and dubby bass by Guto Pryce (ex-SFA). Some instruments have a lounge, almost Latin atmosphere. A summer album I think.
The pattern speaks – SKLOSS
Genre: psychedelic rock [link]
This is heavy sludge rock freak territory with motory drums and mountains of fuzz guitars. Beautiful things. A recommendation from Steve Russell, I’m sure.
Sinister Grift – Panda Bear
Genre: psychedelic [link]
I like the layered vocals which I think are a trademark of Panda Bear. There are strong melodies and sunny vibes in abundance on this record.
Pink Silence – Cloth
Genre: indie rock [link]
I find this album difficult to categorize. It has an ’80s pop vibe in places, but at its core it’s an indie rock album.
Very human qualities – The Bug Club
Genre: Indie pop [link]
A recommendation from Sally Lowell. I enjoyed On The Intricate Inner Workings of the System and their earlier Pure Particles album. This release is wacky, humorous and reminiscent of The Lovely Eggs. They remind me of listening to John Peel’s radio show…
Pando – The Koolies
Genre: Electronics [link]
The other SFA related album on my list. I bought their earlier EPs and wasn’t entirely impressed. Still, I enjoyed this album. They produce great sounds and the production is perfect.
Reissues
I enjoyed these reissues, which came out this year:
- Surfing on sine waves – Polygon window
- Marquee Moon (Rhino High Fidelity vinyl reissue) – Television
- Bound beans – everyone’s coffee beans
The remorse of the list makers
Oh, tomorrow is the last Bandcamp Friday of the year and I’m sure I’ll get my hands on something that I wish I could add to this list. But after putting these together for a few years, I realized it’s best not to obsess over them and just hit publish. Yeah, I didn’t put the Geese album on my list, even though it’s on everyone else’s. I’m sure there will be great albums that I’ve forgotten too. So be it! That was 2025.
—
The title of the post comes from “What’s In The Box (See Whatcha Got)” by The Boo Radleys from their album “C’mon Kids”. I seem to remember this song being about what’s in your record box, although I couldn’t find any confirmation online.
Related
#Whats #box #Packaged #streamed #bloggers


