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()
🎯 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()
🔍 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>
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()
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.
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()
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%