Spotify Listening Data Analysis¶

This notebook analyzes your Spotify listening history to uncover patterns, trends, and insights about your music consumption.

In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from data_loader import load_spotify_data, get_data_summary

plt.style.use('default')
sns.set_palette("husl")

%matplotlib inline
In [2]:
df = load_spotify_data()
summary = get_data_summary(df)

print("Dataset loaded successfully!")
print(f"Shape: {df.shape}")
df.head()
Loading data from combined_data.json...
Loaded 49,050 listening records
Preprocessing data...
Preprocessing complete!
Date range: 2023-06-01 to 2025-10-01
Total listening time: 122547.8 minutes (2042.5 hours)
Dataset loaded successfully!
Shape: (49050, 37)
Out[2]:
ts platform ms_played conn_country ip_addr master_metadata_track_name master_metadata_album_artist_name master_metadata_album_album_name spotify_track_uri episode_name ... year minutes_played seconds_played media_type track_name track_artist album_name is_skip is_complete_play listening_session
0 2023-06-01 01:21:02+00:00 linux 70544 CA 99.250.31.37 How Deep Is Your Love Bee Gees Greatest spotify:track:2JoZzpdeP2G6Csfdq5aLXP None ... 2023 1.175733 70.544 song How Deep Is Your Love Bee Gees Greatest True False 0
1 2023-06-01 01:22:03+00:00 linux 32735 CA 99.250.31.37 Put Your Head On My Shoulder Paul Anka Put Your Head On My Shoulder: The Very Best Of... spotify:track:2DpJ9T2RVRanZcYFHKOAfA None ... 2023 0.545583 32.735 song Put Your Head On My Shoulder Paul Anka Put Your Head On My Shoulder: The Very Best Of... False False 0
2 2023-09-26 13:13:05+00:00 android 0 CA 149.57.28.219 How Deep Is Your Love Bee Gees Greatest spotify:track:2JoZzpdeP2G6Csfdq5aLXP None ... 2023 0.000000 0.000 song How Deep Is Your Love Bee Gees Greatest True False 1
3 2023-09-26 13:13:13+00:00 android 7797 CA 149.57.28.219 Just the Two of Us (feat. Bill Withers) Grover Washington, Jr. Anthology spotify:track:1ko2lVN0vKGUl9zrU0qSlT None ... 2023 0.129950 7.797 song Just the Two of Us (feat. Bill Withers) Grover Washington, Jr. Anthology True False 1
4 2023-09-26 13:13:22+00:00 android 6783 CA 149.57.28.219 Can't Take My Eyes off You Frankie Valli Solo spotify:track:0bfvHnWWOeU1U5XeKyVLbW None ... 2023 0.113050 6.783 song Can't Take My Eyes off You Frankie Valli Solo True False 1

5 rows × 37 columns

📊 Data Overview¶

In [3]:
for key, value in summary.items():
    print(f"{key.replace('_', ' ').title()}: {value}")
Total Tracks: 49050
Unique Tracks: 5386
Unique Artists: 1574
Unique Albums: 2886
Date Range: 2023-06-01 to 2025-10-01
Total Listening Hours: 2042.5
Avg Daily Hours: 3.56
Skip Rate: 38.7
Media Breakdown: {'song': '47958 (97.8%)', 'podcast': '761 (1.6%)', 'audiobook': '331 (0.7%)'}

🎵 Top Artists & Tracks¶

In [4]:
top_artists = df.groupby('track_artist')['minutes_played'].sum().sort_values(ascending=False).head(100)
print('Top 100 Artists by Listening Time:')
print('\n'.join([f"{i+1}. {artist}: {minutes_played:.2f} minutes" for i, (artist, minutes_played) in enumerate(top_artists.items())]))

top_tracks = df.groupby(['track_name', 'track_artist'])['minutes_played'].sum().sort_values(ascending=False).head(100)
track_labels = [f"{track} - {artist}" for (track, artist) in top_tracks.index]
print('\nTop 100 Tracks by Listening Time:')
print('\n'.join([f"{i+1}. {label}: {minutes_played:.2f} minutes" for i, (label, minutes_played) in enumerate(zip(track_labels, top_tracks.values))]))

most_played_artists = df['track_artist'].value_counts().head(100)
print('\nTop 100 Artists by Play Count:')
print('\n'.join([f"{i+1}. {artist}: {count} plays" for i, (artist, count) in enumerate(most_played_artists.items())]))

most_played_tracks = df.groupby(['track_name', 'track_artist']).size().sort_values(ascending=False).head(100)
track_labels = [f"{track} - {artist}" for (track, artist) in most_played_tracks.index]
print('\nTop 100 Tracks by Play Count:')
print('\n'.join([f"{i+1}. {label}: {count} plays" for i, (label, count) in enumerate(zip(track_labels, most_played_tracks.values))]))

top_albums = df.groupby('album_name')['minutes_played'].sum().sort_values(ascending=False).head(100)
print('\nTop 100 Albums by Listening Time:')
print('\n'.join([f"{i+1}. {album}: {minutes_played:.2f} minutes" for i, (album, minutes_played) in enumerate(top_albums.items())]))

most_played_albums = df['album_name'].value_counts().head(100)
print('\nTop 100 Albums by Play Count:')
print('\n'.join([f"{i+1}. {album}: {count} plays" for i, (album, count) in enumerate(most_played_albums.items())]))
Top 100 Artists by Listening Time:
1. Porter Robinson: 37300.22 minutes
2. Laufey: 11284.32 minutes
3. AJR: 9863.55 minutes
4. Radiohead: 9254.84 minutes
5. Ed Sheeran: 3335.29 minutes
6. Twenty One Pilots: 2598.00 minutes
7. The Steve Dangle Podcast: 2556.78 minutes
8. Frank Sinatra: 2347.69 minutes
9. Lex Fridman Podcast: 1892.69 minutes
10. Noah Kahan: 1359.34 minutes
11. Coldplay: 1203.52 minutes
12. Clairo: 1187.47 minutes
13. Jon Bellion: 1150.32 minutes
14. Bill Wurtz: 1145.94 minutes
15. Bo Burnham: 1093.03 minutes
16. Chappell Roan: 948.51 minutes
17. The Joe Rogan Experience: 819.66 minutes
18. Adam Wong: 785.76 minutes
19. Taylor Swift: 696.27 minutes
20. ericdoa: 678.77 minutes
21. Luca Maxim: 624.03 minutes
22. Steve Jobs: 605.35 minutes
23. Wallows: 585.14 minutes
24. Olivia Rodrigo: 523.34 minutes
25. Beyond: 517.71 minutes
26. Arctic Monkeys: 502.44 minutes
27. The Mountain Goats: 496.51 minutes
28. Santo & Johnny: 482.74 minutes
29. JJ Lin: 479.99 minutes
30. Po-uta: 420.65 minutes
31. The Anthropocene Reviewed: 385.25 minutes
32. Alan Tam: 379.04 minutes
33. Everything Is Tuberculosis: 361.10 minutes
34. Stromae: 353.64 minutes
35. Shawn Ryan Show: 338.05 minutes
36. ILLENIUM: 333.57 minutes
37. Leslie Cheung: 326.42 minutes
38. I Hate the Ivy League: 315.59 minutes
39. Avicii: 306.18 minutes
40. MGMT: 281.05 minutes
41. Oakwood Station: 270.47 minutes
42. fun.: 258.45 minutes
43. keudae: 254.54 minutes
44. Queen: 250.34 minutes
45. Gia Margaret: 249.40 minutes
46. The Strokes: 242.71 minutes
47. Tommy Dorsey: 242.52 minutes
48. Modern Wisdom: 241.08 minutes
49. Central Cee: 239.57 minutes
50. Billy Joel: 232.75 minutes
51. Fitz and The Tantrums: 230.95 minutes
52. The Tragically Hip: 227.13 minutes
53. The Weeknd: 221.81 minutes
54. Michael Clifford: 215.94 minutes
55. Madeon: 214.93 minutes
56. boygenius: 214.22 minutes
57. Valley: 212.75 minutes
58. Eason Chan: 211.80 minutes
59. ABBA: 209.49 minutes
60. Dr. Dog: 208.67 minutes
61. 刀郎: 195.94 minutes
62. The Caretaker: 191.25 minutes
63. Leonard Cohen: 190.96 minutes
64. The Rare Occasions: 187.85 minutes
65. Tame Impala: 186.83 minutes
66. beabadoobee: 185.06 minutes
67. The Beatles: 180.25 minutes
68. Vitas: 173.91 minutes
69. Faye Wong: 171.61 minutes
70. Virtual Self: 163.76 minutes
71. Oasis: 161.71 minutes
72. The Jordan B. Peterson Podcast: 161.54 minutes
73. Sam Hui: 157.58 minutes
74. Tape Notes: 151.06 minutes
75. Jonathan Coulton: 149.90 minutes
76. Gorillaz: 146.34 minutes
77. Kamasi Washington: 143.89 minutes
78. sombr: 140.70 minutes
79. The Backseat Lovers: 138.99 minutes
80. Diamond Eyes: 135.58 minutes
81. Kanye West: 129.04 minutes
82. Hozier: 126.88 minutes
83. ImpulsePod: 124.11 minutes
84. My Chemical Romance: 122.92 minutes
85. Elektronomia: 122.13 minutes
86. Lunaar: 122.05 minutes
87. Crosby, Stills, Nash & Young: 121.02 minutes
88. The Killers: 118.52 minutes
89. Beach House: 114.59 minutes
90. Daft Punk: 114.35 minutes
91. Fontaines D.C.: 113.87 minutes
92. Michael Bublé: 112.05 minutes
93. Alphaville: 111.79 minutes
94. Mac Miller: 109.90 minutes
95. Zedd: 107.58 minutes
96. Owl City: 106.99 minutes
97. The Kinks: 106.90 minutes
98. Zach Sang Show: 106.00 minutes
99. Glenn Miller: 102.38 minutes
100. The Tucker Carlson Show: 102.35 minutes

Top 100 Tracks by Listening Time:
1. Russian Roulette - Porter Robinson: 5555.92 minutes
2. Sad Machine - Porter Robinson: 3126.88 minutes
3. Cheerleader - Porter Robinson: 2922.35 minutes
4. Musician - Porter Robinson: 2378.52 minutes
5. Look at the Sky - Porter Robinson: 2097.47 minutes
6. Wind Tempos - Porter Robinson: 1878.15 minutes
7. Trying to Feel Alive - Porter Robinson: 1793.94 minutes
8. No Surprises - Radiohead: 1576.60 minutes
9. Something Comforting - Porter Robinson: 1440.04 minutes
10. Knock Yourself Out XD - Porter Robinson: 1428.13 minutes
11. Next Semester - Twenty One Pilots: 1206.87 minutes
12. Get Your Wish - Porter Robinson: 1159.52 minutes
13. Fake Plastic Trees - Radiohead: 1095.49 minutes
14. Let Down - Radiohead: 1089.38 minutes
15. Everything To Me - Porter Robinson: 1009.56 minutes
16. Creep - Radiohead: 971.64 minutes
17. Kitsune Maison Freestyle - Porter Robinson: 906.54 minutes
18. Is There Really No Happiness? - Porter Robinson: 885.85 minutes
19. Year of the Cup - Porter Robinson: 826.68 minutes
20. do-re-mi-fa-so-la-ti-do - Porter Robinson: 761.69 minutes
21. Easier to Love You - Porter Robinson: 742.42 minutes
22. Cali or Bust - Adam Wong: 724.72 minutes
23. Don't Throw Out My Legos - AJR: 682.35 minutes
24. Everything Goes On - Porter Robinson: 633.71 minutes
25. Mirror - Porter Robinson: 616.33 minutes
26. Perfect Pinterest Garden - Porter Robinson: 583.83 minutes
27. Mother - Porter Robinson: 560.38 minutes
28. Lifelike - Porter Robinson: 543.95 minutes
29. Unfold - Porter Robinson: 534.15 minutes
30. search & destroy - ericdoa: 530.10 minutes
31. Airbag - Radiohead: 521.97 minutes
32. Sweet Time - Porter Robinson: 506.06 minutes
33. Dreamer - Laufey: 500.53 minutes
34. I Wish You Love - Laufey: 492.73 minutes
35. Weird Fishes / Arpeggi - Radiohead: 477.86 minutes
36. Mona Lisa - Porter Robinson: 474.30 minutes
37. dullscythe - Porter Robinson: 452.13 minutes
38. Language - Porter Robinson: 440.62 minutes
39. Fragile - Laufey: 426.63 minutes
40. Like the Movies - Laufey: 425.62 minutes
41. Humansongs - Po-uta: 420.65 minutes
42. Azerbaijan Technology - Luca Maxim: 419.30 minutes
43. Blossom - Porter Robinson: 418.91 minutes
44. 100 Bad Days - AJR: 404.84 minutes
45. Divinity - Porter Robinson: 399.61 minutes
46. Sea Of Voices - Porter Robinson: 360.42 minutes
47. The DJ is Crying for Help - AJR: 354.80 minutes
48. Lovesick - Laufey: 343.51 minutes
49. Maybe Man - AJR: 341.67 minutes
50. Inertia - AJR: 340.92 minutes
51. My Way - 2008 Remastered - Frank Sinatra: 340.02 minutes
52. Karma - AJR: 331.74 minutes
53. Shelter - Porter Robinson: 323.47 minutes
54. Karma Police - Radiohead: 322.98 minutes
55. Paranoid Android - Radiohead: 321.80 minutes
56. #461 – ThePrimeagen: Programming, AI, ADHD, Productivity, Addiction, and God - Lex Fridman Podcast: 320.14 minutes
57. From The Start - Laufey: 312.43 minutes
58. #118 Nick Irving - "The Reaper" - Shawn Ryan Show: 308.45 minutes
59. Street by Street - Laufey: 301.29 minutes
60. Touchy Feely Fool - AJR: 290.51 minutes
61. California and Me - Laufey: 288.12 minutes
62. Come Hang Out - AJR: 287.78 minutes
63. Break My Face - AJR: 287.29 minutes
64. Yellow - Coldplay: 286.94 minutes
65. How to Disappear Completely - Radiohead: 286.60 minutes
66. Promise - Laufey: 282.32 minutes
67. Inertia - Acoustic - AJR: 274.53 minutes
68. Tough Luck - Laufey: 267.33 minutes
69. Falling Behind - Laufey: 264.89 minutes
70. Birthday Party - AJR: 264.59 minutes
71. My Way - Frank Sinatra: 263.23 minutes
72. Christmas in June - AJR: 261.10 minutes
73. Everything In Its Right Place - Radiohead: 256.54 minutes
74. Second Best - Laufey: 255.77 minutes
75. #440 – Pieter Levels: Programming, Viral AI Startups, and Digital Nomad Life - Lex Fridman Podcast: 255.54 minutes
76. All My Love - Noah Kahan: 253.72 minutes
77. Next Up Forever - AJR: 251.81 minutes
78. Sparks - Coldplay: 250.35 minutes
79. Bohemian Rhapsody - Remastered 2011 - Queen: 247.60 minutes
80. While You Were Sleeping - Laufey: 245.68 minutes
81. Valentine - Laufey: 245.28 minutes
82. Years Of War - Porter Robinson: 244.92 minutes
83. The Dumb Song - AJR: 244.00 minutes
84. Haunted - Laufey: 242.81 minutes
85. Navigating - Twenty One Pilots: 241.27 minutes
86. Goodbye To A World - Porter Robinson: 236.79 minutes
87. Above The Chinese Restaurant - Laufey: 232.72 minutes
88. Finale (Can't Wait To See What You Do Next) - AJR: 231.03 minutes
89. Misty - Laufey: 231.01 minutes
90. #310 – Andrew Bustamante: CIA Spy - Lex Fridman Podcast: 229.38 minutes
91. 2085 - AJR: 224.38 minutes
92. Fresh Static Snow - Porter Robinson: 224.16 minutes
93. Beautiful Stranger - Laufey: 220.54 minutes
94. Magnolia - Laufey: 218.40 minutes
95. Bored - Laufey: 212.01 minutes
96. Flicker - Porter Robinson: 210.69 minutes
97. Paladin Strait - Twenty One Pilots: 207.77 minutes
98. Drunk - Ed Sheeran: 206.22 minutes
99. Hinoki Wood - Gia Margaret: 206.02 minutes
100. Night Light - Laufey: 203.42 minutes

Top 100 Artists by Play Count:
1. Porter Robinson: 12060 plays
2. Laufey: 4837 plays
3. AJR: 3821 plays
4. Radiohead: 3061 plays
5. Ed Sheeran: 1247 plays
6. Frank Sinatra: 1203 plays
7. Twenty One Pilots: 1019 plays
8. Bo Burnham: 819 plays
9. Bill Wurtz: 665 plays
10. Jon Bellion: 522 plays
11. Noah Kahan: 508 plays
12. Clairo: 483 plays
13. Coldplay: 434 plays
14. ericdoa: 417 plays
15. Santo & Johnny: 389 plays
16. Chappell Roan: 383 plays
17. Luca Maxim: 337 plays
18. Adam Wong: 318 plays
19. Taylor Swift: 297 plays
20. Wallows: 287 plays
21. The Mountain Goats: 277 plays
22. Olivia Rodrigo: 259 plays
23. Stromae: 218 plays
24. Arctic Monkeys: 217 plays
25. JJ Lin: 202 plays
26. The Steve Dangle Podcast: 189 plays
27. Beyond: 188 plays
28. Gia Margaret: 182 plays
29. ILLENIUM: 178 plays
30. Lex Fridman Podcast: 166 plays
31. Leslie Cheung: 162 plays
32. Central Cee: 162 plays
33. Po-uta: 157 plays
34. Alan Tam: 144 plays
35. The Weeknd: 141 plays
36. keudae: 140 plays
37. Avicii: 136 plays
38. Oakwood Station: 130 plays
39. The Beatles: 126 plays
40. ABBA: 126 plays
41. Tommy Dorsey: 121 plays
42. fun.: 120 plays
43. Steve Jobs: 119 plays
44. MGMT: 113 plays
45. Madeon: 106 plays
46. Vitas: 103 plays
47. Valley: 102 plays
48. Michael Clifford: 99 plays
49. Jonathan Coulton: 93 plays
50. Fitz and The Tantrums: 92 plays
51. boygenius: 92 plays
52. Charli xcx: 90 plays
53. Eason Chan: 89 plays
54. beabadoobee: 88 plays
55. Billy Joel: 88 plays
56. Toby Fox: 88 plays
57. Kanye West: 87 plays
58. The Caretaker: 87 plays
59. The Rare Occasions: 86 plays
60. 刀郎: 85 plays
61. The Strokes: 85 plays
62. Tame Impala: 84 plays
63. Faye Wong: 80 plays
64. Sam Hui: 79 plays
65. Queen: 78 plays
66. Virtual Self: 75 plays
67. Øneheart: 74 plays
68. Michael Bublé: 74 plays
69. Lunaar: 74 plays
70. The Anthropocene Reviewed: 73 plays
71. Scatman John: 73 plays
72. The Tragically Hip: 73 plays
73. Mac Miller: 72 plays
74. Dr. Dog: 66 plays
75. Leonard Cohen: 66 plays
76. Hozier: 65 plays
77. Daft Punk: 64 plays
78. Elektronomia: 64 plays
79. Woody Guthrie: 64 plays
80. Lonzo & Oscar: 63 plays
81. My Chemical Romance: 62 plays
82. Everything Is Tuberculosis: 61 plays
83. Miles Away: 59 plays
84. Dave Wang: 58 plays
85. sombr: 58 plays
86. Gorillaz: 58 plays
87. The Killers: 55 plays
88. Weezer: 55 plays
89. Modern Wisdom: 53 plays
90. Crosby, Stills, Nash & Young: 52 plays
91. The Walters: 52 plays
92. Dimash Qudaibergen: 51 plays
93. Shawn Mendes: 51 plays
94. Glenn Miller: 50 plays
95. Jay Chou: 49 plays
96. The Joe Rogan Experience: 49 plays
97. The Backseat Lovers: 49 plays
98. Laurent Paquin: 49 plays
99. Oasis: 49 plays
100. I Hate the Ivy League: 48 plays

Top 100 Tracks by Play Count:
1. Russian Roulette - Porter Robinson: 1185 plays
2. Cheerleader - Porter Robinson: 951 plays
3. Sad Machine - Porter Robinson: 749 plays
4. Musician - Porter Robinson: 732 plays
5. Knock Yourself Out XD - Porter Robinson: 670 plays
6. Look at the Sky - Porter Robinson: 517 plays
7. No Surprises - Radiohead: 505 plays
8. Trying to Feel Alive - Porter Robinson: 488 plays
9. Wind Tempos - Porter Robinson: 464 plays
10. Lifelike - Porter Robinson: 464 plays
11. Perfect Pinterest Garden - Porter Robinson: 434 plays
12. Get Your Wish - Porter Robinson: 425 plays
13. Something Comforting - Porter Robinson: 400 plays
14. Is There Really No Happiness? - Porter Robinson: 361 plays
15. Next Semester - Twenty One Pilots: 355 plays
16. do-re-mi-fa-so-la-ti-do - Porter Robinson: 351 plays
17. Everything To Me - Porter Robinson: 340 plays
18. search & destroy - ericdoa: 318 plays
19. Creep - Radiohead: 313 plays
20. Kitsune Maison Freestyle - Porter Robinson: 312 plays
21. Fake Plastic Trees - Radiohead: 286 plays
22. Easier to Love You - Porter Robinson: 284 plays
23. Year of the Cup - Porter Robinson: 278 plays
24. Cali or Bust - Adam Wong: 257 plays
25. Let Down - Radiohead: 252 plays
26. Everything Goes On - Porter Robinson: 251 plays
27. I Wish You Love - Laufey: 233 plays
28. Mother - Porter Robinson: 219 plays
29. Don't Throw Out My Legos - AJR: 216 plays
30. Azerbaijan Technology - Luca Maxim: 204 plays
31. Mona Lisa - Porter Robinson: 199 plays
32. Dreamer - Laufey: 190 plays
33. Like the Movies - Laufey: 185 plays
34. Blossom - Porter Robinson: 178 plays
35. Unfold - Porter Robinson: 175 plays
36. Mirror - Porter Robinson: 175 plays
37. dullscythe - Porter Robinson: 171 plays
38. 100 Bad Days - AJR: 171 plays
39. Sweet Time - Porter Robinson: 170 plays
40. Fragile - Laufey: 165 plays
41. Hinoki Wood - Gia Margaret: 159 plays
42. Humansongs - Po-uta: 157 plays
43. Karma Police - Radiohead: 155 plays
44. Airbag - Radiohead: 153 plays
45. From The Start - Laufey: 146 plays
46. Years Of War - Porter Robinson: 144 plays
47. Maybe Man - AJR: 139 plays
48. Shelter - Porter Robinson: 136 plays
49. Content - Bo Burnham: 134 plays
50. Lovesick - Laufey: 128 plays
51. The DJ is Crying for Help - AJR: 128 plays
52. Falling Behind - Laufey: 125 plays
53. Divinity - Porter Robinson: 120 plays
54. Inertia - AJR: 119 plays
55. Weird Fishes / Arpeggi - Radiohead: 114 plays
56. Valentine - Laufey: 113 plays
57. While You Were Sleeping - Laufey: 112 plays
58. Sea Of Voices - Porter Robinson: 111 plays
59. Street by Street - Laufey: 110 plays
60. Lucky - Radiohead: 109 plays
61. Language - Porter Robinson: 109 plays
62. Promise - Laufey: 108 plays
63. Break My Face - AJR: 106 plays
64. Sparks - Coldplay: 105 plays
65. Karma - AJR: 103 plays
66. Birthday Party - AJR: 102 plays
67. Touchy Feely Fool - AJR: 101 plays
68. Backslide - Twenty One Pilots: 101 plays
69. Lucky for Me - Laufey: 100 plays
70. Silver Lining - Laufey: 99 plays
71. Inertia - Acoustic - AJR: 99 plays
72. Second Best - Laufey: 97 plays
73. Come Hang Out - AJR: 96 plays
74. Navigating - Twenty One Pilots: 95 plays
75. Everything In Its Right Place - Radiohead: 94 plays
76. Tough Luck - Laufey: 94 plays
77. The Dumb Song - AJR: 93 plays
78. Misty - Laufey: 93 plays
79. California and Me - Laufey: 92 plays
80. Haunted - Laufey: 92 plays
81. James - Laufey: 92 plays
82. My Way - 2008 Remastered - Frank Sinatra: 91 plays
83. Next Up Forever - AJR: 88 plays
84. Bored - Laufey: 88 plays
85. Beautiful Stranger - Laufey: 88 plays
86. Magnolia - Laufey: 87 plays
87. Fluorescent Adolescent - Arctic Monkeys: 85 plays
88. Slow Down - Laufey: 85 plays
89. Paranoid Android - Radiohead: 84 plays
90. Beats - AJR: 84 plays
91. Call My Dad - AJR: 83 plays
92. All My Love - Noah Kahan: 81 plays
93. Above The Chinese Restaurant - Laufey: 81 plays
94. Must Be Love - Laufey: 81 plays
95. La de Da de Da de Da de Day Oh - Bill Wurtz: 81 plays
96. Goodbye To A World - Porter Robinson: 81 plays
97. Burn the House Down - AJR: 80 plays
98. Yellow - Coldplay: 80 plays
99. Questions For The Universe - Laufey: 79 plays
100. Flicker - Porter Robinson: 78 plays

Top 100 Albums by Listening Time:
1. Nurture: 15081.17 minutes
2. SMILE! :D: 14591.09 minutes
3. Podcast Episode: 7026.27 minutes
4. Worlds: 5376.04 minutes
5. OK Computer: 4875.24 minutes
6. Neotheater: 3295.70 minutes
7. Everything I Know About Love: 2919.87 minutes
8. The Maybe Man: 2507.77 minutes
9. Clancy: 2487.37 minutes
10. Bewitched: 2395.89 minutes
11. Typical of Me EP: 1841.67 minutes
12. Audiobook: 1763.17 minutes
13. OK ORCHESTRA: 1732.23 minutes
14. +: 1513.41 minutes
15. Bewitched: The Goddess Edition: 1477.53 minutes
16. The Bends: 1403.26 minutes
17. Stick Season: 1348.82 minutes
18. INSIDE (DELUXE): 1053.20 minutes
19. The Human Condition: 1023.16 minutes
20. Charm: 903.41 minutes
21. KID A MNESIA: 896.85 minutes
22. x: 867.94 minutes
23. Pablo Honey: 806.69 minutes
24. The Click - Deluxe Edition: 803.22 minutes
25. The Rise and Fall of a Midwest Princess: 797.86 minutes
26. A Matter of Time: 796.84 minutes
27. Parachutes: 731.12 minutes
28. Cali or Bust: 724.72 minutes
29. The Click: 707.05 minutes
30. In Rainbows: 699.05 minutes
31. Everything Goes On: 640.39 minutes
32. search & destroy: 521.27 minutes
33. A Night At The Symphony: 509.21 minutes
34. Russian Roulette: 485.54 minutes
35. GUTS: 484.36 minutes
36. Language: 446.18 minutes
37. Humansongs: 420.65 minutes
38. Azerbaijan Technology: 419.30 minutes
39. Nothing But The Best: 411.99 minutes
40. Creep EP: 405.18 minutes
41. ÷: 334.41 minutes
42. Shelter: 323.47 minutes
43. Favourite Worst Nightmare: 287.05 minutes
44. Oracular Spectacular: 277.43 minutes
45. My Way: 275.45 minutes
46. Inertia: 274.53 minutes
47. Fearless (Taylor's Version): 269.78 minutes
48. =: 266.48 minutes
49. Parkside Lane: 261.10 minutes
50. Some Nights: 254.13 minutes
51. ASCEND: 251.68 minutes
52. Romantic Piano: 249.40 minutes
53. A Night At The Opera: 247.66 minutes
54. The Sunset Tree: 235.47 minutes
55. racine carrée: 232.46 minutes
56. Nurture (The Piano Album): 230.83 minutes
57. "和自己對話" 實驗專輯: 229.33 minutes
58. The New Abnormal: 228.96 minutes
59. Platinum: 223.94 minutes
60. AM: 215.28 minutes
61. Nothing Happens: 214.74 minutes
62. the record: 214.22 minutes
63. Tough Luck: 212.58 minutes
64. The Essential Frank Sinatra with the Tommy Dorsey Orchestra (with Frank Sinatra): 211.44 minutes
65. Shame, Shame: 205.32 minutes
66. La de Da de Da de Da de Day Oh: 201.41 minutes
67. Nothing But The Best - 2008 Remastered: 199.31 minutes
68. Living Room: 197.98 minutes
69. A Very Laufey Holiday: 193.05 minutes
70. An empty bliss beyond this World: 184.41 minutes
71. Pulling Leaves Off Trees: 178.05 minutes
72. Currents: 174.61 minutes
73. Good Faith: 172.56 minutes
74. 風繼續吹: 169.73 minutes
75. 第二天堂: 167.53 minutes
76. More Than Just a Dream: 164.62 minutes
77. Философия чуда: 162.09 minutes
78. A Rush of Blood to the Head: 158.40 minutes
79. Notion: 155.76 minutes
80. Ghost Stories: 154.16 minutes
81. Nearly Cried Of Happiness: 153.84 minutes
82. The World We Knew: 149.90 minutes
83. Cheerleader: 149.44 minutes
84. Immunity: 142.79 minutes
85. The Epic: 142.78 minutes
86. Virtual Self: 141.21 minutes
87. Let You Break My Heart Again: 138.37 minutes
88. kill me for always (feat. porter robinson): 137.06 minutes
89. CAN'T RUSH GREATNESS: 136.68 minutes
90. When We Were Friends: 135.79 minutes
91. True: 131.20 minutes
92. Plastic Beach: 130.78 minutes
93. Lover Girl: 128.81 minutes
94. Thing a Week Three: 126.66 minutes
95. Knock Yourself Out XD: 126.35 minutes
96. Water the Flowers, Pray for a Garden: 124.14 minutes
97. Long Walk Home: 121.54 minutes
98. Deja Vu: 121.02 minutes
99. A Night To Remember: 118.16 minutes
100. (What's The Story) Morning Glory? (Deluxe Remastered Edition): 117.97 minutes

Top 100 Albums by Play Count:
1. Nurture: 4906 plays
2. SMILE! :D: 4797 plays
3. OK Computer: 1591 plays
4. Worlds: 1570 plays
5. Everything I Know About Love: 1241 plays
6. Neotheater: 1214 plays
7. Clancy: 960 plays
8. Bewitched: 952 plays
9. The Maybe Man: 931 plays
10. Typical of Me EP: 811 plays
11. INSIDE (DELUXE): 786 plays
12. Podcast Episode: 761 plays
13. OK ORCHESTRA: 619 plays
14. Bewitched: The Goddess Edition: 597 plays
15. +: 546 plays
16. Stick Season: 500 plays
17. The Bends: 466 plays
18. The Human Condition: 441 plays
19. Charm: 349 plays
20. The Click: 339 plays
21. Audiobook: 331 plays
22. x: 323 plays
23. The Rise and Fall of a Midwest Princess: 321 plays
24. A Matter of Time: 320 plays
25. search & destroy: 313 plays
26. The Click - Deluxe Edition: 303 plays
27. KID A MNESIA: 279 plays
28. Parachutes: 268 plays
29. Pablo Honey: 268 plays
30. Cali or Bust: 257 plays
31. Everything Goes On: 254 plays
32. A Night At The Symphony: 250 plays
33. In Rainbows: 225 plays
34. GUTS: 212 plays
35. Azerbaijan Technology: 204 plays
36. Romantic Piano: 182 plays
37. Humansongs: 157 plays
38. Platinum: 146 plays
39. Creep EP: 146 plays
40. ÷: 145 plays
41. Nothing But The Best: 143 plays
42. Shelter: 136 plays
43. ASCEND: 133 plays
44. Nurture (The Piano Album): 130 plays
45. Parkside Lane: 126 plays
46. Favourite Worst Nightmare: 123 plays
47. Russian Roulette: 122 plays
48. Some Nights: 117 plays
49. racine carrée: 117 plays
50. The Sunset Tree: 117 plays
51. A Very Laufey Holiday: 115 plays
52. Living Room: 115 plays
53. Nothing Happens: 114 plays
54. Oracular Spectacular: 111 plays
55. Language: 110 plays
56. Fearless (Taylor's Version): 107 plays
57. =: 106 plays
58. The Essential Frank Sinatra with the Tommy Dorsey Orchestra (with Frank Sinatra): 101 plays
59. Inertia: 99 plays
60. Nearly Cried Of Happiness: 96 plays
61. "和自己對話" 實驗專輯: 96 plays
62. AM: 93 plays
63. Философия чуда: 93 plays
64. the record: 92 plays
65. Tough Luck: 90 plays
66. UNDERTALE Soundtrack: 87 plays
67. An empty bliss beyond this World: 83 plays
68. The World We Knew: 83 plays
69. Long Walk Home: 83 plays
70. La de Da de Da de Da de Day Oh: 81 plays
71. CAN'T RUSH GREATNESS: 80 plays
72. Currents: 79 plays
73. Nothing But The Best - 2008 Remastered: 75 plays
74. A Jolly Christmas From Frank Sinatra: 75 plays
75. Good Faith: 74 plays
76. My Way: 74 plays
77. Scatman's World: 73 plays
78. The New Abnormal: 73 plays
79. After Hours: 72 plays
80. Thing a Week Three: 70 plays
81. A Night At The Opera: 70 plays
82. Notion: 69 plays
83. Pulling Leaves Off Trees: 68 plays
84. Immunity: 67 plays
85. Water the Flowers, Pray for a Garden: 66 plays
86. Presenting Santo & Johnny: 66 plays
87. Shame, Shame: 65 plays
88. Let You Break My Heart Again: 65 plays
89. Virtual Self: 63 plays
90. Offer Rescinded: 61 plays
91. Whiplash (Original Motion Picture Soundtrack): 61 plays
92. kill me for always (feat. porter robinson): 60 plays
93. Lover Girl: 60 plays
94. Bring Me Back: 59 plays
95. This Land is Your Land: The Asch Recordings, Vol. 1: 59 plays
96. More Than Just a Dream: 58 plays
97. Frank Sinatra New Christmas Mono to Stereo Mixes: 58 plays
98. 第二天堂: 57 plays
99. The Black Parade: 57 plays
100. Where or When: 56 plays

⏰ Listening Patterns Over Time¶

In [5]:
daily_listening = df.groupby('date')['minutes_played'].sum().reset_index()

fig = px.line(daily_listening, x='date', y='minutes_played', 
              title='Daily Listening Time Over Time',
              labels={'minutes_played': 'Minutes Played', 'date': 'Date'})
fig.update_layout(height=400)
fig.show()
In [6]:
fig, axes = plt.subplots(2, 2, figsize=(15, 10))

hourly_listening = df.groupby('hour')['minutes_played'].sum()
hourly_listening.plot(kind='bar', ax=axes[0,0], color='purple', alpha=0.7)
axes[0,0].set_title('Listening by Hour of Day')
axes[0,0].set_xlabel('Hour')
axes[0,0].set_ylabel('Minutes Played')
axes[0,0].tick_params(axis='x', rotation=0)

day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
daily_listening = df.groupby('day_of_week')['minutes_played'].sum().reindex(day_order)
daily_listening.plot(kind='bar', ax=axes[0,1], color='orange', alpha=0.7)
axes[0,1].set_title('Listening by Day of Week')
axes[0,1].set_xlabel('Day')
axes[0,1].set_ylabel('Minutes Played')
axes[0,1].tick_params(axis='x', rotation=45)

monthly_listening = df.groupby('month')['minutes_played'].sum()
monthly_listening.plot(kind='bar', ax=axes[1,0], color='green', alpha=0.7)
axes[1,0].set_title('Listening by Month')
axes[1,0].set_xlabel('Month')
axes[1,0].set_ylabel('Minutes Played')
axes[1,0].tick_params(axis='x', rotation=45)

skip_by_hour = df.groupby('hour')['is_skip'].mean() * 100
skip_by_hour.plot(kind='line', ax=axes[1,1], color='red', marker='o')
axes[1,1].set_title('Skip Rate by Hour of Day')
axes[1,1].set_xlabel('Hour')
axes[1,1].set_ylabel('Skip Rate (%)')

plt.tight_layout()
plt.show()
No description has been provided for this image

🎯 Listening Behavior Analysis¶

In [7]:
fig, axes = plt.subplots(2, 2, figsize=(15, 10))

df['minutes_played'].hist(bins=50, ax=axes[0,0], alpha=0.7, color='blue')
axes[0,0].set_title('Distribution of Track Listening Duration')
axes[0,0].set_xlabel('Minutes Played')
axes[0,0].set_ylabel('Frequency')
axes[0,0].axvline(df['minutes_played'].mean(), color='red', linestyle='--', label=f'Mean: {df["minutes_played"].mean():.1f} min')
axes[0,0].legend()

platform_counts = df['platform'].value_counts()
platform_counts.plot(kind='pie', ax=axes[0,1], autopct='%1.1f%%')
axes[0,1].set_title('Listening by Platform')
axes[0,1].set_ylabel('')

skip_reasons = df[df['is_skip']]['reason_end'].value_counts().head(10)
skip_reasons.plot(kind='barh', ax=axes[1,0], color='salmon')
axes[1,0].set_title('Top Skip Reasons')
axes[1,0].set_xlabel('Count')

session_lengths = df.groupby('listening_session')['minutes_played'].sum()
session_lengths.hist(bins=30, ax=axes[1,1], alpha=0.7, color='green')
axes[1,1].set_title('Distribution of Listening Session Lengths')
axes[1,1].set_xlabel('Session Length (minutes)')
axes[1,1].set_ylabel('Frequency')
axes[1,1].axvline(session_lengths.mean(), color='red', linestyle='--', label=f'Mean: {session_lengths.mean():.1f} min')
axes[1,1].legend()

plt.tight_layout()
plt.show()
No description has been provided for this image

🔍 Advanced Analysis¶

In [8]:
artist_diversity = df.groupby('date')['track_artist'].nunique().reset_index()
artist_diversity.columns = ['date', 'unique_artists']

fig = px.scatter(artist_diversity, x='date', y='unique_artists',
                title='Musical Diversity Over Time (Unique Artists per Day)',
                labels={'unique_artists': 'Unique Artists', 'date': 'Date'})
fig.update_layout(height=400)
fig.show()
In [9]:
repeat_listening = df.groupby(['track_name', 'track_artist']).agg({
    'minutes_played': 'sum',
    'ts': 'count'
}).rename(columns={'ts': 'play_count'}).reset_index()

repeat_listening['avg_minutes_per_play'] = repeat_listening['minutes_played'] / repeat_listening['play_count']

fig = px.scatter(repeat_listening, 
                x='play_count', 
                y='minutes_played',
                size='avg_minutes_per_play',
                hover_data=['track_name', 'track_artist'],
                title='Track Popularity vs Total Listening Time',
                labels={'play_count': 'Number of Plays', 'minutes_played': 'Total Minutes Played'})
fig.update_layout(height=500)
fig.show()

📈 Custom Analysis¶

Use the cells below for your own custom analysis!

In [10]:
print("Available columns for analysis:")
print(df.columns.tolist())
Available columns for analysis:
['ts', 'platform', 'ms_played', 'conn_country', 'ip_addr', 'master_metadata_track_name', 'master_metadata_album_artist_name', 'master_metadata_album_album_name', 'spotify_track_uri', 'episode_name', 'episode_show_name', 'spotify_episode_uri', 'audiobook_title', 'audiobook_uri', 'audiobook_chapter_uri', 'audiobook_chapter_title', 'reason_start', 'reason_end', 'shuffle', 'skipped', 'offline', 'offline_timestamp', 'incognito_mode', 'date', 'hour', 'day_of_week', 'month', 'year', 'minutes_played', 'seconds_played', 'media_type', 'track_name', 'track_artist', 'album_name', 'is_skip', 'is_complete_play', 'listening_session']
In [11]:
# First, create the week_number column if it doesn't exist
if 'week_number' not in df.columns:
    df['week_number'] = df['ts'].dt.isocalendar().week

# Calculate average daily, weekly, monthly and yearly listening time
daily_avg_listening_time = df.groupby('date')['minutes_played'].sum().mean()
weekly_avg_listening_time = df.groupby('week_number')['minutes_played'].sum().mean()
monthly_avg_listening_time = df.groupby('month')['minutes_played'].sum().mean()
yearly_avg_listening_time = df.groupby('year')['minutes_played'].sum().mean()

print('Average daily listening time: {:.2f} hours'.format(daily_avg_listening_time/60))
print('Average weekly listening time: {:.2f} hours'.format(weekly_avg_listening_time/60))
print('Average monthly listening time: {:.2f} hours'.format(monthly_avg_listening_time/60))
print('Average yearly listening time: {:.2f} hours'.format(yearly_avg_listening_time/60))
Average daily listening time: 3.46 hours
Average weekly listening time: 39.28 hours
Average monthly listening time: 170.21 hours
Average yearly listening time: 680.82 hours
In [12]:
top_10_artists = df.groupby('track_artist')['minutes_played'].sum().sort_values(ascending=False).head(20)
fig, ax = plt.subplots(figsize=(10,6))
ax.set_title('My Top 10 Artists: Cumulative Listening Time Over Time')
ax.set_xlabel('Date')
ax.set_ylabel('Cumulative Minutes Played')
for artist in top_10_artists.index[:10]:
    streams = df[df.track_artist == artist].groupby('date')['minutes_played'].sum()
    cumulative_streams = streams.cumsum()
    ax.plot(cumulative_streams.index, cumulative_streams.values, label=artist)
ax.legend()
Out[12]:
<matplotlib.legend.Legend at 0x1167b0e10>
No description has been provided for this image
In [13]:
fig, ax = plt.subplots(figsize=(10,6))

# Group by date and calculate daily totals
daily_listening = df.groupby('date')['minutes_played'].sum()
daily_artists = df.groupby('date')['track_artist'].nunique()

# Calculate cumulative listening time
cumulative_listening = daily_listening.cumsum()

# Calculate cumulative unique artists
cumulative_artists = daily_artists.cumsum()

# Plot cumulative listening time on primary y-axis
ax.plot(cumulative_listening.index, cumulative_listening.values, color='blue', label='Total Listening Time (Cumulative)')
ax.set_xlabel('Date')
ax.set_ylabel('Total Listening Time (minutes)', color='blue')
ax.tick_params(axis='y', labelcolor='blue')

# Create secondary y-axis for cumulative unique artists
ax2 = ax.twinx()
ax2.plot(cumulative_artists.index, cumulative_artists.values, color='red', label='Unique Artists (Cumulative)')
ax2.set_ylabel('Number of Unique Artists (Cumulative)', color='red')
ax2.tick_params(axis='y', labelcolor='red')

# Add legends
ax.legend(loc='upper left')
ax2.legend(loc='upper right')

plt.title('Cumulative Listening Time and Cumulative Artist Discovery Over Time')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
No description has been provided for this image
In [14]:
porter_df = df[df['track_artist'] == 'Porter Robinson'].copy()

print(f"Porter Robinson Analysis")
print(f"Total listening time: {porter_df['minutes_played'].sum():.1f} minutes ({porter_df['minutes_played'].sum()/60:.1f} hours)")
print(f"Total plays: {len(porter_df):,}")
print(f"Unique songs: {porter_df['track_name'].nunique()}")
print(f"Unique albums: {porter_df['album_name'].nunique()}")
print(f"Date range: {porter_df['date'].min()} to {porter_df['date'].max()}")

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('Porter Robinson Listening Analysis', fontsize=16)

porter_albums = porter_df.groupby('album_name')['minutes_played'].sum().sort_values(ascending=False)
print(f"\nTop Albums by Listening Time:")
for i, (album, minutes) in enumerate(porter_albums.head(10).items()):
    print(f"{i+1}. {album}: {minutes:.1f} minutes")

for album in porter_albums.index[:5]:
    album_streams = porter_df[porter_df['album_name'] == album].groupby('date')['minutes_played'].sum()
    cumulative_album = album_streams.cumsum()
    ax1.plot(cumulative_album.index, cumulative_album.values, label=album, linewidth=2)
ax1.set_title('Cumulative Album Listening Time')
ax1.set_xlabel('Date')
ax1.set_ylabel('Cumulative Minutes')
ax1.legend()
ax1.grid(True, alpha=0.3)

porter_songs = porter_df.groupby('track_name')['minutes_played'].sum().sort_values(ascending=False)
print(f"\nTop Songs by Listening Time:")
for i, (song, minutes) in enumerate(porter_songs.head(10).items()):
    print(f"{i+1}. {song}: {minutes:.1f} minutes")

for song in porter_songs.index[:10]:
    song_streams = porter_df[porter_df['track_name'] == song].groupby('date')['minutes_played'].sum()
    cumulative_song = song_streams.cumsum()
    ax2.plot(cumulative_song.index, cumulative_song.values, label=song, linewidth=2)
ax2.set_title('Cumulative Top Songs Listening Time')
ax2.set_xlabel('Date')
ax2.set_ylabel('Cumulative Minutes')
ax2.legend()
ax2.grid(True, alpha=0.3)

daily_stats = porter_df.groupby('date').agg({
    'minutes_played': 'sum',
    'track_name': 'count'
}).rename(columns={'track_name': 'plays'})

weekly_stats = porter_df.groupby(porter_df['ts'].dt.to_period('W')).agg({
    'minutes_played': 'sum',
    'track_name': 'count'
}).rename(columns={'track_name': 'plays'})

monthly_stats = porter_df.groupby(porter_df['ts'].dt.to_period('M')).agg({
    'minutes_played': 'sum',
    'track_name': 'count'
}).rename(columns={'track_name': 'plays'})

yearly_stats = porter_df.groupby('year').agg({
    'minutes_played': 'sum',
    'track_name': 'count'
}).rename(columns={'track_name': 'plays'})

ax3.plot(daily_stats.index, daily_stats['minutes_played'], alpha=0.7, label='Daily Minutes')
ax3.set_title('Daily Listening Minutes')
ax3.set_xlabel('Date')
ax3.set_ylabel('Minutes Played')
ax3.grid(True, alpha=0.3)

ax4.plot(daily_stats.index, daily_stats['plays'], alpha=0.7, color='orange', label='Daily Plays')
ax4.set_title('Daily Play Count')
ax4.set_xlabel('Date')
ax4.set_ylabel('Number of Plays')
ax4.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print(f"\nAverage Statistics:")
print(f"Average minutes per day: {daily_stats['minutes_played'].mean():.1f}")
print(f"Average plays per day: {daily_stats['plays'].mean():.1f}")
print(f"Average minutes per week: {weekly_stats['minutes_played'].mean():.1f}")
print(f"Average plays per week: {weekly_stats['plays'].mean():.1f}")
print(f"Average minutes per month: {monthly_stats['minutes_played'].mean():.1f}")
print(f"Average plays per month: {monthly_stats['plays'].mean():.1f}")

print(f"\nYearly Breakdown:")
for year, stats in yearly_stats.iterrows():
    print(f"{year}: {stats['minutes_played']:.1f} minutes ({stats['minutes_played']/60:.1f} hours), {stats['plays']} plays")

print(f"\nListening by Day of Week:")
dow_stats = porter_df.groupby('day_of_week').agg({
    'minutes_played': 'mean',
    'track_name': 'count'
}).rename(columns={'track_name': 'total_plays'})
day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
for day in day_order:
    if day in dow_stats.index:
        print(f"{day}: {dow_stats.loc[day, 'minutes_played']:.1f} avg minutes/day, {dow_stats.loc[day, 'total_plays']} total plays")

print(f"\nListening by Hour:")
hour_stats = porter_df.groupby('hour').agg({
    'minutes_played': 'sum',
    'track_name': 'count'
}).rename(columns={'track_name': 'plays'})
peak_hour = hour_stats['minutes_played'].idxmax()
print(f"Peak listening hour: {peak_hour}:00 ({hour_stats.loc[peak_hour, 'minutes_played']:.1f} total minutes)")
print(f"Most active hours (top 5):")
for hour, stats in hour_stats.sort_values('minutes_played', ascending=False).head(5).iterrows():
    print(f"  {hour}:00 - {stats['minutes_played']:.1f} minutes, {stats['plays']} plays")
Porter Robinson Analysis
Total listening time: 37300.2 minutes (621.7 hours)
Total plays: 12,060
Unique songs: 70
Unique albums: 20
Date range: 2024-04-13 to 2025-10-01

Top Albums by Listening Time:
1. Nurture: 15081.2 minutes
2. SMILE! :D: 14591.1 minutes
3. Worlds: 5376.0 minutes
4. Everything Goes On: 633.7 minutes
5. Russian Roulette: 463.2 minutes
6. Language: 440.6 minutes
7. Shelter: 323.5 minutes
8. Cheerleader: 130.6 minutes
9. Knock Yourself Out XD: 115.3 minutes
10. Musician: 57.2 minutes

Top Songs by Listening Time:
1. Russian Roulette: 5555.9 minutes
2. Sad Machine: 3126.9 minutes
3. Cheerleader: 2922.4 minutes
4. Musician: 2378.5 minutes
5. Look at the Sky: 2097.5 minutes
6. Wind Tempos: 1878.2 minutes
7. Trying to Feel Alive: 1793.9 minutes
8. Something Comforting: 1440.0 minutes
9. Knock Yourself Out XD: 1428.1 minutes
10. Get Your Wish: 1159.5 minutes
/var/folders/kh/kff3nbhs1zl422ky9tgx8jp00000gn/T/ipykernel_95885/2324406366.py:48: UserWarning:

Converting to PeriodArray/Index representation will drop timezone information.

/var/folders/kh/kff3nbhs1zl422ky9tgx8jp00000gn/T/ipykernel_95885/2324406366.py:53: UserWarning:

Converting to PeriodArray/Index representation will drop timezone information.

No description has been provided for this image
Average Statistics:
Average minutes per day: 91.6
Average plays per day: 29.6
Average minutes per week: 556.7
Average plays per week: 180.0
Average minutes per month: 1963.2
Average plays per month: 634.7

Yearly Breakdown:
2024: 22428.5 minutes (373.8 hours), 7491.0 plays
2025: 14871.7 minutes (247.9 hours), 4569.0 plays

Listening by Day of Week:
Monday: 3.1 avg minutes/day, 1695 total plays
Tuesday: 2.9 avg minutes/day, 1888 total plays
Wednesday: 3.1 avg minutes/day, 1825 total plays
Thursday: 2.9 avg minutes/day, 1789 total plays
Friday: 3.1 avg minutes/day, 1806 total plays
Saturday: 3.3 avg minutes/day, 1645 total plays
Sunday: 3.2 avg minutes/day, 1412 total plays

Listening by Hour:
Peak listening hour: 15:00 (3259.6 total minutes)
Most active hours (top 5):
  15:00 - 3259.6 minutes, 996.0 plays
  2:00 - 2917.9 minutes, 1031.0 plays
  1:00 - 2895.2 minutes, 985.0 plays
  19:00 - 2802.8 minutes, 873.0 plays
  18:00 - 2702.6 minutes, 836.0 plays
In [15]:
# Load data first (run this if df is not defined)
df = load_spotify_data()
summary = get_data_summary(df)

# Separate data for 2024 and 2025
df_2024 = df[df['year'] == 2024]
df_2025 = df[df['year'] == 2025]

print("=== SPOTIFY LISTENING STATS FOR 2024 ===\n")

# 2024 Overall stats
print("2024 Overall Stats:")
print(f"Total listening records: {len(df_2024):,}")
print(f"Total listening time: {df_2024['minutes_played'].sum():.1f} minutes ({df_2024['minutes_played'].sum()/60:.1f} hours)")
print(f"Unique tracks: {df_2024['spotify_track_uri'].nunique():,}")
print(f"Unique artists: {df_2024['track_artist'].nunique():,}")
print(f"Skip rate: {df_2024['is_skip'].mean()*100:.1f}%")

print("\n=== TOP ARTISTS (2024) ===")
top_artists_2024 = df_2024.groupby('track_artist')['minutes_played'].sum().sort_values(ascending=False).head(10)
for i, (artist, minutes) in enumerate(top_artists_2024.items(), 1):
    hours = minutes / 60
    print(f"{i:2d}. {artist}: {hours:.1f} hours ({minutes:.0f} minutes)")

print("\n=== TOP TRACKS (2024) ===")
top_tracks_2024 = df_2024.groupby(['track_name', 'track_artist'])['minutes_played'].sum().sort_values(ascending=False).head(10)
for i, ((track, artist), minutes) in enumerate(top_tracks_2024.items(), 1):
    hours = minutes / 60
    print(f"{i:2d}. {track} by {artist}: {hours:.1f} hours ({minutes:.0f} minutes)")

print("\n=== MONTHLY LISTENING TRENDS (2024) ===")
monthly_2024 = df_2024.groupby('month')['minutes_played'].sum().sort_values(ascending=False)
for month, minutes in monthly_2024.items():
    hours = minutes / 60
    print(f"{month}: {hours:.1f} hours")

print("\n" + "="*60)
print("=== SPOTIFY LISTENING STATS FOR 2025 ===\n")

# 2025 Overall stats
print("2025 Overall Stats:")
print(f"Total listening records: {len(df_2025):,}")
print(f"Total listening time: {df_2025['minutes_played'].sum():.1f} minutes ({df_2025['minutes_played'].sum()/60:.1f} hours)")
print(f"Unique tracks: {df_2025['spotify_track_uri'].nunique():,}")
print(f"Unique artists: {df_2025['track_artist'].nunique():,}")
print(f"Skip rate: {df_2025['is_skip'].mean()*100:.1f}%")

print("\n=== TOP ARTISTS (2025) ===")
top_artists_2025 = df_2025.groupby('track_artist')['minutes_played'].sum().sort_values(ascending=False).head(10)
for i, (artist, minutes) in enumerate(top_artists_2025.items(), 1):
    hours = minutes / 60
    print(f"{i:2d}. {artist}: {hours:.1f} hours ({minutes:.0f} minutes)")

print("\n=== TOP TRACKS (2025) ===")
top_tracks_2025 = df_2025.groupby(['track_name', 'track_artist'])['minutes_played'].sum().sort_values(ascending=False).head(10)
for i, ((track, artist), minutes) in enumerate(top_tracks_2025.items(), 1):
    hours = minutes / 60
    print(f"{i:2d}. {track} by {artist}: {hours:.1f} hours ({minutes:.0f} minutes)")

print("\n=== MONTHLY LISTENING TRENDS (2025) ===")
monthly_2025 = df_2025.groupby('month')['minutes_played'].sum().sort_values(ascending=False)
for month, minutes in monthly_2025.items():
    hours = minutes / 60
    print(f"{month}: {hours:.1f} hours")

print("\n" + "="*60)
print("=== YEAR COMPARISON ===")
print(f"2024 Total: {df_2024['minutes_played'].sum()/60:.1f} hours")
print(f"2025 Total: {df_2025['minutes_played'].sum()/60:.1f} hours")
print(f"Difference: {(df_2025['minutes_played'].sum() - df_2024['minutes_played'].sum())/60:.1f} hours")
Loading data from combined_data.json...
Loaded 49,050 listening records
Preprocessing data...
Preprocessing complete!
Date range: 2023-06-01 to 2025-10-01
Total listening time: 122547.8 minutes (2042.5 hours)
=== SPOTIFY LISTENING STATS FOR 2024 ===

2024 Overall Stats:
Total listening records: 23,851
Total listening time: 57832.4 minutes (963.9 hours)
Unique tracks: 3,060
Unique artists: 826
Skip rate: 32.0%

=== TOP ARTISTS (2024) ===
 1. Porter Robinson: 373.8 hours (22429 minutes)
 2. AJR: 144.6 hours (8676 minutes)
 3. Laufey: 103.0 hours (6181 minutes)
 4. Twenty One Pilots: 34.8 hours (2086 minutes)
 5. Radiohead: 34.2 hours (2052 minutes)
 6. Ed Sheeran: 29.4 hours (1762 minutes)
 7. Frank Sinatra: 26.1 hours (1564 minutes)
 8. Bo Burnham: 15.2 hours (913 minutes)
 9. Jon Bellion: 14.5 hours (868 minutes)
10. Adam Wong: 12.6 hours (756 minutes)

=== TOP TRACKS (2024) ===
 1. Russian Roulette by Porter Robinson: 54.8 hours (3286 minutes)
 2. Sad Machine by Porter Robinson: 33.1 hours (1985 minutes)
 3. Cheerleader by Porter Robinson: 31.1 hours (1865 minutes)
 4. Musician by Porter Robinson: 25.2 hours (1514 minutes)
 5. Wind Tempos by Porter Robinson: 16.9 hours (1012 minutes)
 6. Something Comforting by Porter Robinson: 16.7 hours (1004 minutes)
 7. Look at the Sky by Porter Robinson: 16.5 hours (989 minutes)
 8. Knock Yourself Out XD by Porter Robinson: 16.4 hours (982 minutes)
 9. Next Semester by Twenty One Pilots: 15.9 hours (953 minutes)
10. Trying to Feel Alive by Porter Robinson: 15.2 hours (911 minutes)

=== MONTHLY LISTENING TRENDS (2024) ===
December: 179.9 hours
September: 165.9 hours
October: 156.2 hours
November: 131.2 hours
May: 106.8 hours
August: 61.7 hours
April: 45.5 hours
January: 27.0 hours
July: 26.7 hours
March: 26.1 hours
June: 18.8 hours
February: 18.0 hours

============================================================
=== SPOTIFY LISTENING STATS FOR 2025 ===

2025 Overall Stats:
Total listening records: 24,553
Total listening time: 63658.5 minutes (1061.0 hours)
Unique tracks: 3,129
Unique artists: 985
Skip rate: 44.7%

=== TOP ARTISTS (2025) ===
 1. Porter Robinson: 247.9 hours (14872 minutes)
 2. Radiohead: 120.0 hours (7203 minutes)
 3. Laufey: 82.0 hours (4921 minutes)
 4. The Steve Dangle Podcast: 42.6 hours (2557 minutes)
 5. Lex Fridman Podcast: 29.5 hours (1767 minutes)
 6. Ed Sheeran: 26.2 hours (1574 minutes)
 7. AJR: 19.8 hours (1188 minutes)
 8. Clairo: 19.6 hours (1177 minutes)
 9. Noah Kahan: 16.5 hours (993 minutes)
10. Bill Wurtz: 15.1 hours (906 minutes)

=== TOP TRACKS (2025) ===
 1. Russian Roulette by Porter Robinson: 37.8 hours (2269 minutes)
 2. Sad Machine by Porter Robinson: 19.0 hours (1142 minutes)
 3. Look at the Sky by Porter Robinson: 18.5 hours (1109 minutes)
 4. Fake Plastic Trees by Radiohead: 18.3 hours (1095 minutes)
 5. Cheerleader by Porter Robinson: 17.6 hours (1057 minutes)
 6. Let Down by Radiohead: 17.1 hours (1024 minutes)
 7. Trying to Feel Alive by Porter Robinson: 14.7 hours (883 minutes)
 8. Wind Tempos by Porter Robinson: 14.4 hours (866 minutes)
 9. Musician by Porter Robinson: 14.4 hours (865 minutes)
10. Creep by Radiohead: 12.8 hours (769 minutes)

=== MONTHLY LISTENING TRENDS (2025) ===
January: 144.2 hours
February: 138.8 hours
July: 136.1 hours
August: 129.9 hours
March: 123.4 hours
April: 111.9 hours
June: 99.0 hours
September: 87.3 hours
May: 85.8 hours
October: 4.5 hours

============================================================
=== YEAR COMPARISON ===
2024 Total: 963.9 hours
2025 Total: 1061.0 hours
Difference: 97.1 hours
In [16]:
# Media Type Analysis
print("="*60)
print("MEDIA TYPE BREAKDOWN")
print("="*60)

media_counts = df['media_type'].value_counts()
media_percentages = (df['media_type'].value_counts(normalize=True) * 100).round(1)
media_time = df.groupby('media_type')['minutes_played'].sum()

for media_type in media_counts.index:
    count = media_counts[media_type]
    percentage = media_percentages[media_type]
    total_hours = media_time[media_type] / 60
    print(f"{media_type.title()}: {count:,} plays ({percentage}%) - {total_hours:.1f} hours")

# Verification that "Unknown Artist" issue is fixed
unknown_count = (df['track_artist'] == 'Unknown Artist').sum()
print(f"\n✅ Records with 'Unknown Artist': {unknown_count}")

print("\nSample entries by media type:")
for media_type in df['media_type'].unique():
    sample = df[df['media_type'] == media_type].iloc[0]
    print(f"\n{media_type.upper()}:")
    print(f"  Title: {sample['track_name']}")
    print(f"  Artist/Show: {sample['track_artist']}")
    print(f"  Album: {sample['album_name']}")
============================================================
MEDIA TYPE BREAKDOWN
============================================================
Song: 47,958 plays (97.8%) - 1896.0 hours
Podcast: 761 plays (1.6%) - 117.1 hours
Audiobook: 331 plays (0.7%) - 29.4 hours

✅ Records with 'Unknown Artist': 0

Sample entries by media type:

SONG:
  Title: How Deep Is Your Love
  Artist/Show: Bee Gees
  Album: Greatest

PODCAST:
  Title: Mozart 2x Speed Left Ear , Bible In Chinese Right Ear
  Artist/Show: 04Roker's Uploads
  Album: Podcast Episode

AUDIOBOOK:
  Title: Opening Credits
  Artist/Show: The Art of War
  Album: Audiobook
In [17]:
# Media Type Visualizations
fig, axes = plt.subplots(1, 3, figsize=(18, 6))

# Play count distribution
axes[0].pie(media_counts.values, labels=[f"{label}\n({count:,} plays)" for label, count in zip(media_counts.index, media_counts.values)], 
           autopct='%1.1f%%', startangle=90)
axes[0].set_title('Media Types by Play Count')

# Listening time distribution
axes[1].pie(media_time.values, labels=[f"{label}\n({time/60:.0f} hrs)" for label, time in zip(media_time.index, media_time.values)], 
           autopct='%1.1f%%', startangle=90)
axes[1].set_title('Media Types by Listening Time')

# Average session length
avg_session = df.groupby('media_type')['minutes_played'].mean()
bars = axes[2].bar(avg_session.index, avg_session.values, color=['#1f77b4', '#ff7f0e', '#2ca02c'])
axes[2].set_title('Average Session Length by Media Type')
axes[2].set_ylabel('Minutes')
axes[2].tick_params(axis='x', rotation=45)

for bar, value in zip(bars, avg_session.values):
    axes[2].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1, 
                f'{value:.1f}m', ha='center', va='bottom')

plt.tight_layout()
plt.show()
No description has been provided for this image
In [18]:
# Top Content by Media Type
print("="*60)
print("TOP CONTENT BY MEDIA TYPE")
print("="*60)

for media_type in ['song', 'podcast', 'audiobook']:
    if media_type in df['media_type'].values:
        print(f"\n--- TOP {media_type.upper()} ---")
        media_df = df[df['media_type'] == media_type]
        
        if media_type == 'song':
            top_content = media_df.groupby(['track_name', 'track_artist'])['minutes_played'].sum().sort_values(ascending=False).head(5)
            for i, ((track, artist), minutes) in enumerate(top_content.items(), 1):
                print(f"{i}. {track} by {artist}: {minutes:.1f} minutes")
        
        elif media_type == 'podcast':
            top_content = media_df.groupby(['track_name', 'track_artist'])['minutes_played'].sum().sort_values(ascending=False).head(5)
            for i, ((episode, show), minutes) in enumerate(top_content.items(), 1):
                print(f"{i}. {episode} from {show}: {minutes:.1f} minutes")
        
        elif media_type == 'audiobook':
            top_content = media_df.groupby(['track_artist'])['minutes_played'].sum().sort_values(ascending=False).head(5)
            for i, (book, minutes) in enumerate(top_content.items(), 1):
                print(f"{i}. {book}: {minutes:.1f} minutes")

# Skip rates by media type
skip_rates = df.groupby('media_type')['is_skip'].mean() * 100
print(f"\n" + "="*60)
print("SKIP RATES BY MEDIA TYPE")
print("="*60)
for media_type, skip_rate in skip_rates.items():
    print(f"{media_type.title()}: {skip_rate:.1f}%")
============================================================
TOP CONTENT BY MEDIA TYPE
============================================================

--- TOP SONG ---
1. Russian Roulette by Porter Robinson: 5555.9 minutes
2. Sad Machine by Porter Robinson: 3126.9 minutes
3. Cheerleader by Porter Robinson: 2922.4 minutes
4. Musician by Porter Robinson: 2378.5 minutes
5. Look at the Sky by Porter Robinson: 2097.5 minutes

--- TOP PODCAST ---
1. #461 – ThePrimeagen: Programming, AI, ADHD, Productivity, Addiction, and God from Lex Fridman Podcast: 320.1 minutes
2. #118 Nick Irving - "The Reaper" from Shawn Ryan Show: 308.4 minutes
3. #440 – Pieter Levels: Programming, Viral AI Startups, and Digital Nomad Life from Lex Fridman Podcast: 255.5 minutes
4. #310 – Andrew Bustamante: CIA Spy from Lex Fridman Podcast: 229.4 minutes
5. #922 - Naval Ravikant - 44 Harsh Truths About Human Nature from Modern Wisdom: 196.1 minutes

--- TOP AUDIOBOOK ---
1. Steve Jobs: 605.4 minutes
2. The Anthropocene Reviewed: 385.2 minutes
3. Everything Is Tuberculosis: 361.1 minutes
4. I Hate the Ivy League: 315.6 minutes
5. The Metamorphosis: 69.7 minutes

============================================================
SKIP RATES BY MEDIA TYPE
============================================================
Audiobook: 32.6%
Podcast: 72.1%
Song: 38.2%