Introduction

To celebrate the 1,000th episode of A State of Trance the radioshow invited viewers to vote for their all-time favorite trance tracks, and the resulting list was broadcast as ASOT 1000.

In this post we'll analyze the top 1,000 - which artists, BPMs, and years are most-represented? And more!

Some Housekeeping

As with previous posts here, we'll be pulling data from Spotify and graphing the results. While there is an official "ASOT Top 1000" playlist on Spotify, I'm opting to instead use the "ASOT TOP 1000 Countdown Extended" playlist compiled by reddit user turbodevin. As Devin writes,

I used a filler track (4 seconds) for the missing song, to keep the song numbers corresponding to the ranking. When an extended version was not available, a shorter version is used. When a remix is not available, the regular version is used when available. MISSING

531 || Sean Callery - The Longest Day (Armin van Buuren Remix)

REMIX NOT AVAILABLE

414 || Faithless - Insomnia (Andrew Rayel Remix)

520 || Safri Duo - Played A Live (The Bongo Song) [NWYR & Willem de Roo Remix]

530 || Kensington - Sorry (Armin van Buuren Remix)

635 || Ilse de Lange - The Great Escape (Armin van Buuren Remix)

661 || Zedd feat. Foxes - Clarity (Andrew Rayel Remix)

While the playlist may not be complete, I'd still consider to be the most-complete playlist available on Spotify - using extended mixes over the official playlist's radio mixes is certainly more preferrable, at least.

Remember, all data here is pulled directly from Spotify's API without any modification from my end. See the post on Methodology for details on what data we can pull from Spotify, and how. Notably, Spotify's AudioFeaturesObject lists tempo as "overall estimated tempo of a track in beats per minute (BPM)" - keyword being estimate. I've done little to account for any inconsistencies and nothing to address them! Spotify's API for "Get a Playlist's Items" limits us to getting 100 tracks at a time. Let's make 10 API calls for 100 tracks each, incrementing offset each time, and save the results.

"""
User: https://open.spotify.com/user/113444659
Playlist: ASOT TOP 1000 Countdown Extended
Playlist link: https://open.spotify.com/playlist/5DCcjCLMlPjTwKLCcYyzIj
Playlist ID: 5DCcjCLMlPjTwKLCcYyzIj
"""
top_1000_playlist = '5DCcjCLMlPjTwKLCcYyzIj'

top_1000_tracks = []

# Get full details of the tracks and episodes of a playlis
# https://spotipy.readthedocs.io/en/2.16.1/#spotipy.client.Spotify.playlist_items
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=100)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=200)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=300)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=400)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=500)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=600)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=700)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=800)['items'])
top_1000_tracks.extend(sp.playlist_tracks(top_1000_playlist, offset=900)['items'])
print(len(top_1000_tracks))
1000

What's number 1?

print(top_1000_tracks[999]['track']['artists'][0]['name'], '-', top_1000_tracks[999]['track']['name'])
Armin van Buuren - Shivers

Artists

Let's begin by looking at the artists who made the top 1000 - how many unique artists were featured?

unique_artists = set()

for track in top_1000_tracks:
    for artist in track['track']['artists']:
            unique_artists.add(artist['name'])      

print(len(unique_artists))
639

Which artists were featured the most?

from collections import defaultdict

artist_counter = defaultdict(int)

for track in top_1000_tracks:
    for artist in track['track']['artists']:
         artist_counter[artist['name']] += 1


top_artists = sorted(artist_counter.items(), key=lambda k_v: k_v[1], reverse=True)

Alright, let's see the top 25 in a graph..

source = pd.DataFrame.from_dict(top_artists[:25])

bars = alt.Chart(source).mark_bar().encode(
    x=alt.X('1:Q', title='Plays'),
    y=alt.Y('0:N', sort='-x', title='Artist')
).properties(
    title="ASOT Top 1000 - Most-played artists",
    width=600
)

text = bars.mark_text(
    align='left',
    baseline='middle',
    dx=3  # Nudges text to right so it doesn't appear on top of the bar
).encode(
    text='1:Q'
)

bars + text

No surprise at who the #1 is, but the sheer number of their tracks featured is pretty impressive - over 10% of the ASOT Top 1000 was produced by Armin van Buuren, more than twice the number of the second-most featured artist!

Which artists were featured exactly once, with what track, at what position?

# Find all artists with one play, then find that track in the top 1000
for artist in top_artists:
    if artist[1] == 1:
        for position, track in enumerate(top_1000_tracks):
            if track['track']['artists'][0]['name'] == artist[0]:
                print(1000 - position, '.', track['track']['artists'][0]['name'], '-', track['track']['name'])

997 . ATN - Miss A Day - Original Mix
996 . Late Night Alumni - Empty Streets - Lumïsade Balearic Mix
993 . Ron van den Beuken - Timeless - Ron van den Beuken Remix
992 . Greg Downey - These Hands I Hold - Sean Tyas Remix
991 . M.I.K.E. - Chocolate Infusion - Original Mix
989 . Adam Nickey - Never Gone - Original Mix [Above & Beyond Respray]
987 . Salt Tank - Eugina - Michael Woods Remix
981 . A Force - Crystal Dawn [ASOT 254] - A Tribute To '99 Remix
969 . Myon & Shane 54 - Ibiza Sunrise - Classic Dub
967 . Neptune Project - Aztec - Original Mix
964 . Ava Mea - In The End - Original Mix
963 . Rodg - High On Life - Extended Mix
962 . Midway - Monkey Forest - Original Mix Edit
961 . Ramin Djawadi - Game Of Thrones Theme - Armin van Buuren Extended Remix
960 . Filterheadz - Yimanya - Original Mix
950 . Jody Wisternoff - The Bridge - Chicane Rework
943 . Probspot - Foreplay - Original Mix
940 . Selu Vibra - Stargazing [ASOT 224] - Original Mix
928 . Ernesto vs. Bastian - Dark Side Of The Moon - Original Extended
927 . Sensation - The Anthem 2003 - Original Mix
922 . Space RockerZ - Puzzle Piece - Daniel Heatcliff's Farewell Remix
911 . Sunset Bros - I'm Feeling It (In The Air) - Sunset Bros X Mark McCabe / MaRLo Remix
906 . Avao - Cosmic Order - Extended Mix
902 . Elysian - Beyond The Comfort Zone - Extended Mix
901 . Lemon - Anticipation
899 . Luigi Lusini - Who We Are
897 . Thomas Bronzwaer - Shadow World - Original Mix
896 . Shane - Too Late To Turn - Armin Van Buuren Remix
887 . Niels Van Gogh - Pulverturm (Original)
884 . Swedish House Mafia - One
883 . Monogato - Miami Vibe - Omnia Remix
879 . Eximinds - Lacrimosa - Extended Mix
855 . Full Tilt - Surrender - Sneijder vs John O'Callaghan Remix
854 . Carl B. - Social Suicide [ASOT 261] - Original Mix
853 . Tinlicker - Sleepwalker - Extended Mix
851 . Cass - Perception - New Vocal Mix
850 . Bjorn Akesson - Painting Pyramids - Original Mix
848 . Frank T.R.A.X. - Nebuchan - Radion6 Extended Remix
844 . Easton - Healing Rain - Bryan Kearney Remix
843 . Arisen Flame - Explorer - Original Mix
841 . Astrix - Deep Jungle Walk
840 . Sodality - Challenger - Extended Mix
834 . Hazem Beltagui - We Are - Original Mix
830 . 3LAU - Tokyo (feat. XIRA) - Fatum Remix
828 . The Space Brothers - Shine - Jorn van Deynhoven Extended Remix
822 . Ferry Corsten's Countdown - More Than Anything (Listener's Choice) (CC489) - Stoneface & Terminal Remix
821 . Simon O'Shine & Adam Navel - Marathon - Simon O'Shine Mix
819 . Humate - Love Stimulation - Love-Club-Mix
813 . A & Z - Fleeting Moments - Original Mix
810 . Abstract Vision - Conqueror - Original Mix
798 . Arctic Quest - Renaissance - Original Mix
797 . K90 - Red Snapper - Allen Watts Extended Remix
796 . Ali Wilson - Pandora - Original
790 . Fragile - Inertia - Armin Van Buuren Remix
783 . Audien - Wayfarer - Original Mix
778 . Holden - Nothing - 93 Returning Remix
777 . Art Of Trance - Madagascar - Ferry Corsten Remix
773 . Synergy - Hello Strings - Original Mix
771 . Jase Thirlwall - Dust - Extended Mix
770 . Antillas - Damaged - Main Mix
768 . Coast 2 Coast - Home - Original Mix
760 . Hammer - Language - Santiago Nino Dub Tech Mix
759 . Nuera - Green Cape Sunset - Original Mix
748 . Riva - Time Is The Healer - Armin van Buuren Extended Dub
747 . Joop - The Future - Original Mix
733 . Jeremy Vancaulart - Hurt - Extended Mix
714 . Randy Katana - In Silence - Txitxarro Mix
712 . London Grammar - Hey Now - Arty Remix
711 . Parker & Hanson - Gravity - Original Mix
710 . Claudia Cazacu - Freefalling - Original Mix
709 . Dutch Force - Deadline - Original Mix
703 . Fragma - Toca Me - Inpetto 2008 Mix
702 . James Dymond - Spectrum - Extended Mix
701 . DRYM - Smile - Extended Mix
698 . Peter Martin - Perfect Wave - Original Mix
696 . Active Sight - Out Of Our Lives - Original Mix
694 . Glenn Morrison - Contact
687 . Zirenz - Edge of Space Ultimate - Whiteroom Remix
684 . 7 Skies - Central Park - Original Mix
683 . Filo & Peri - Anthem - Original Radio Version
682 . Carl B - All Day - Original
678 . Dimitri Vegas & Like Mike - Repeat After Me - Extended Mix
672 . Aria - Dido - Armin Van Buuren's Universal Religion Mix
670 . Perry O'Neil - Wave Force - Original Mix
669 . Dumonde - Never Look Back - Full On Vocal Mix
665 . Jon O'Bir - Found A Way (feat. Fisher) - Joint Operations Centre Remix
657 . Cressida - 6AM - Kyau & Albert Update
655 . Zoo Brazil - You Can Have It All - George Acosta Remix
650 . PRAANA - Kaleidoscope - Extended Mix
647 . Bedrock - Heaven Scent - Original Mix
643 . Eco - A Million Sounds, A Thousand Smiles - Original Mix
641 . Ultra Shock - The Sound Of E - Jorn van Deynhoven Extended Remix
635 . Ilse DeLange - The Great Escape
621 . Mauro Picotto - Lizard - Cosmic Gate Extended Remix
612 . Monolink - Return to Oz - ARTBAT Remix
607 . Karen Overton - Your Loving Arms - Club Mix
596 . Primer - Everlast - Original Mix
587 . Age Of Love - The Age Of Love - Jam & Spoon Watch Out For Stella Mix
584 . Ascension - Someone - Giuseppe Ottaviani Extended Remix
583 . Masters & Nickson - Out There - Robert Nickson 2016 Extended Remix
581 . GTR - Mistral
580 . Tim Berg - Bromance - Avicii’s Arena Mix
571 . Jan Johnston - Flesh - DJ Tiesto Mix
567 . Artento Divini - A.D.D.A. - Extended Mix
566 . Freefall - Skydive
554 . Beatsole - Before I Wake - Extended Mix
553 . The Noble Six - Firewalker - Extended Mix
549 . Electrovoya - Effervesce
542 . Tillmann Uhrmacher - On the Run - Ocean to Shore Club Extended
538 . Chakra - Home - Above & Beyond Mix
532 . Members Of Mayday - 10 In 01 - Paul Van Dyk Club Mix
531 . Kyuss - Yeah
526 . Ohmna - Key Of Life - MaRLo Remix
520 . Safri Duo - Played-A-Live (The Bongo Song)
511 . Chris Lake - Carry Me Away (feat. Emma Hewitt)
508 . Ayla - Ayla - Ben Nicky & Luke Bond Remix
507 . Michael Dow - Ascent
505 . Afternova - Serenity - Andy Blueman Remix
499 . York - The Reachers of Civilisation
488 . Conjure One - Tears From The Moon - Tiësto In Search Of Sunrise Remix
485 . Paul Webster - Time - Sean Tyas Dub Mix
481 . 3rd Moon - Monsun - Original Mix
480 . Solid Sessions - Janeiro - Armin van Buuren Remix
478 . Quench - Dreams - Nicholson's Extended Cathedral Remix
472 . Insigma - Open Our Eyes - Original Mix
467 . VER:WEST - 5 Seconds Before Sunrise
465 . Moogwai - Viola - Armin van Buuren Remix
458 . Albert Vorne - Formentera What - Gareth Emery Remix
457 . Vast Vision - Everything - Aly & Fila Remix
451 . Phynn - Lucid
440 . Fred Baker - Confirmation
438 . Tilt - The World Doesn't Know - Original Mix
435 . Cerf, Mitiska & Jaren - You Never Said - Dash Berlin Remix
434 . Triple A - Winter Stayed - Armin van Buuren's On the Beach Mix
433 . XiJaro & Pitch - Whispers Of Time - Extended Mix
417 . 16BL - Chant A Tune - Jeremy Olander Remix
399 . 2nd Phase - Never Come Down - Norin & Rad Remix
392 . Matlock - Deep Psychosis - Daniel Kandi's Cure Mix
387 . Wiegel Meirmans Snitker - Nova Zembla - Armin van Buuren Extended Remix
386 . Graham Bell - The Sound Of Letting Go (Tribute To Yotam) - Extended Mix
385 . Hans Zimmer - He's A Pirate - Tiësto Orchestral Mix
380 . Atlantis - Fiji - ReOrder Extended Remix
374 . Svenson & Gielen - Twisted - Original Mix
371 . Galen Behr - Carabella - Galen Behr vs Orjan Nilsen Extended Remix
370 . 8 Wonders - The Morning After - The Thrillseekers Remix
369 . Basic Dawn - Pure Thrust - NU NRG Remix
355 . Nalin & Kane - Beachball - Original Club Mix
342 . Rex Mundi - Nothing At All - Original Mix
323 . L.S.G. - Netherworld - Oliver Prime Remix
321 . DJ's United - Remember Love - Original Mix
301 . M.I.D.O.R. & Six4eight - No Man's Land
294 . KhoMha - Tierra - Extended Mix
273 . Jean-Michel Jarre - Stardust
271 . DJ Aligator - The Perfect Match - Club Mix
270 . Akesson - Perfect Blue - Original Mix
255 . iio - Rapture (Armin Van Buuren Remix Remastered) [feat. Nadia Ali]
243 . Headstrong - Tears - Aurosonic Progressive Mix
241 . Pulser - My Religion - Original Mix
236 . Major League - Wonder Where You Are?
233 . Sasha - Xpander
228 . Alt+F4 - Alt+F4 - Original Mix
222 . Ridgewalkers - Find - Andy Moor Remix
217 . Eyal Barkan - Voyage - Magikal Remake
184 . The Doppler Effect - Beauty Hides In The Deep - John O'Callaghan Extended
183 . FUTURECODE - Dancing In The Rain - Extended Mix
177 . Ayumi Hamasaki - M - Above & Beyond Remix
170 . Sonic Inc - The Taste Of Summer - Fire & Ice Vital Remix
167 . Darude - Sandstorm
147 . Salt Lake - Rendezvous - Original Mix
120 . Darren Tate - Let The Light Shine In - 12'' Original Mix
113 . William Orbit - Barber's Adagio for Strings - Ferry Corsten Remix
110 . Yuri Kane - Right Back - Original Extended
104 . Sergey Nevone - Apprehension - Aly & Fila Mix
80 . Reflekt - Need To Feel Loved - Thrillseekers Remix
71 . Binary Finary - 1998 - Gouryella Remix
70 . Planet Perfecto Knights - Resurection
59 . Arksun - Arisen - Original Mix
54 . Saltwater - The Legacy - Alphazone Extended Remix
44 . Energy 52 - Café Del Mar - Three 'n One Remix
43 . Luminary - Amsterdam - Smith & Pledger Remix
37 . Key4050 - I Love You - Extended Mix
33 . Robert Miles - Children - Dream Version
21 . Veracocha - Carte Blanche - Original Mix
18 . Motorcycle - As The Rush Comes - Gabriel & Dresden Sweeping Strings Mix
4 . Delerium - Silence (feat. Sarah McLachlan) - DJ Tiësto's in Search of Sunrise Remix

Quite a few, click "Show Output" above to view! Note that we're only listing the artist on the track credits that's only featured on that track. For example, "120. Darren Tate & Jono Grant – Shine (Let The Light Shine In)" is listed here but lists only Darren Tate as the producer because Jono Grant also appears in "562. Jono Grant vs Mike Koglin – Circuits".

Tracks

Let's looks at some track-specific numbers now.

In which years were the tracks produced?

annual_total = defaultdict(int)

for track in top_1000_tracks:
    annual_total[track['track']['album']['release_date'][:4]] += 1

top_years = sorted(annual_total.items(), key=lambda k_v: k_v[1])
print(top_years)
[('1992', 1), ('1995', 1), ('1997', 2), ('1996', 5), ('1998', 5), ('1999', 7), ('2002', 12), ('2001', 14), ('2000', 18), ('2003', 23), ('2004', 25), ('2005', 32), ('2006', 37), ('2007', 38), ('2008', 45), ('2017', 49), ('2015', 50), ('2014', 54), ('2020', 55), ('2016', 55), ('2010', 56), ('2009', 62), ('2013', 64), ('2018', 64), ('2011', 66), ('2012', 68), ('2019', 92)]

In a graph:

source = pd.DataFrame.from_dict(top_years)

bars = alt.Chart(source).mark_bar().encode(
    x=alt.X('1:Q', title='Plays'),
    y=alt.Y('0:N', sort='-x', title='Year')
).properties(
    title="ASOT Top 1000 - Most-represented years",
    width=600
)

text = bars.mark_text(
    align='left',
    baseline='middle',
    dx=3  # Nudges text to right so it doesn't appear on top of the bar
).encode(
    text='1:Q'
)

bars + text

Might be better to see it sorted by year:

source = pd.DataFrame.from_dict(top_years)

bars = alt.Chart(source).mark_bar().encode(
    x=alt.X('1:Q', title='Plays'),
    y=alt.Y('0:N', title='Year')
).properties(
    title="ASOT Top 1000 - Yearly representation",
    width=600
)

text = bars.mark_text(
    align='left',
    baseline='middle',
    dx=3  # Nudges text to right so it doesn't appear on top of the bar
).encode(
    text='1:Q'
)

bars + text

What are the oldest tracks in the list? Sorted by position.

for position, track in enumerate(top_1000_tracks):
    if int(track['track']['album']['release_date'][:4]) < 2000:
        track_artist = track['track']['artists'][0]['name']
        for artist in track['track']['artists'][1:]:
            track_artist += " & " + artist['name']
        print(1000 - position, '.', track_artist, '-', track['track']['name'], '- released', track['track']['album']['release_date'])
983 . Vincent de Moor - Flowtation - released 1996-01-01
887 . Niels Van Gogh - Pulverturm (Original) - released 1998-09-21
691 . Lost Witness & Lange - Happiness Happening - Lange Remix - released 1999-03-22
647 . Bedrock - Heaven Scent - Original Mix - released 1999-12-01
638 . Agnelli & Nelson - El Niño - released 1998-08-01
566 . Freefall - Skydive - released 1998-01-01
531 . Kyuss - Yeah - released 1992-01-01
499 . York - The Reachers of Civilisation - released 1999-10-20
414 . Faithless & Rollo Armstrong & Sister Bliss - Insomnia - released 1996
355 . Nalin & Kane - Beachball - Original Club Mix - released 1997-01-01
233 . Sasha - Xpander - released 1999-07-05
167 . Darude - Sandstorm - released 1999-01-01
127 . Three Drives On A Vinyl - Greece 2000 - Original Mix - released 1997-06-01
125 . ATB - 9 Pm - Till I Come - released 1998-10-26
113 . William Orbit & Ferry Corsten - Barber's Adagio for Strings - Ferry Corsten Remix - released 1995-01-17
99 . Armin van Buuren - Blue Fear - Original Mix - released 1996-03-10
91 . Cygnus X & Rank 1 - Superstring - Rank 1 Remix - released 1996
87 . Solarstone & Armin van Buuren - Seven Cities - Armin van Buuren Remix - released 1999-06-04
71 . Binary Finary & Gouryella - 1998 - Gouryella Remix - released 1998
33 . Robert Miles - Children - Dream Version - released 1996-07-16
21 . Veracocha - Carte Blanche - Original Mix - released 1999-05-01

A lot of tracks released in 2020 made the list, what are the most recent? Here's the tracks in the months leading up to the end of the year.

for position, track in enumerate(top_1000_tracks):
    if track['track']['album']['release_date'][:7] == '2020-09' or track['track']['album']['release_date'][:7] == '2020-10' or track['track']['album']['release_date'][:7] == '2020-11' or track['track']['album']['release_date'][:7] == '2020-12':
        track_artist = track['track']['artists'][0]['name']
        for artist in track['track']['artists'][1:]:
            track_artist += " & " + artist['name']
        print(1000 - position, '.', track_artist, '-', track['track']['name'], '- released', track['track']['album']['release_date'])
915 . Assaf & Cassandra Grey - Lost At Sea - released 2020-12-11
893 . Cosmic Gate & Andrew Bayer - The Launch - Extended Mix - released 2020-09-04
863 . ARTY & NK - Who Am I - released 2020-11-27
836 . Allen Watts & Gid Sedgwick - Another You - Extended Mix - released 2020-09-11
824 . Giuseppe Ottaviani & Sue McLaren - Not One Goodbye - Extended Mix - released 2020-09-25
446 . Above & Beyond & Zoë Johnston & Kyau & Albert - You Got To Go - Kyau & Albert Extended Mix - released 2020-10-01
205 . Aly & Fila & Plumb - Somebody Loves You - Extended Mix - released 2020-09-25

In which years were the tracks produced by the top five most-played artists produced?

artist_avb_counter = defaultdict(int) # Tracks crediting Armin van Buuren
artist_ab_counter = defaultdict(int) # Tracks crediting Above & Beyond
artist_af_counter = defaultdict(int) # Tracks crediting Aly & Fila
artist_fc_counter = defaultdict(int) # Tracks crediting Ferry Corsten
artist_ar_counter = defaultdict(int) # Tracks crediting Andrew Rayel

for track in top_1000_tracks:
    for artist in track['track']['artists']:
        if artist['name'] == "Armin van Buuren":
            artist_avb_counter[track['track']['album']['release_date'][:4]] += 1
        elif artist['name'] == "Above & Beyond":
            artist_ab_counter[track['track']['album']['release_date'][:4]] += 1
        elif artist['name'] == "Aly & Fila":
            artist_af_counter[track['track']['album']['release_date'][:4]] += 1
        elif artist['name'] == "Ferry Corsten":
            artist_fc_counter[track['track']['album']['release_date'][:4]] += 1
        elif artist['name'] == "Andrew Rayel":
            artist_ar_counter[track['track']['album']['release_date'][:4]] += 1

# Sort by year and print the results
sorted_avb_years = sorted(artist_avb_counter.items(), key=lambda k_v: k_v[0])
sorted_ab_years = sorted(artist_ab_counter.items(), key=lambda k_v: k_v[0])
sorted_af_years = sorted(artist_af_counter.items(), key=lambda k_v: k_v[0])
sorted_fc_years = sorted(artist_fc_counter.items(), key=lambda k_v: k_v[0])
sorted_ar_years = sorted(artist_ar_counter.items(), key=lambda k_v: k_v[0])

print("Armin van Buuren:")
print(sorted_avb_years)
print("Above & Beyond:")
print(sorted_ab_years)
print("Aly & Fila:")
print(sorted_af_years)
print("Ferry Corsten:")
print(sorted_fc_years)
print("Andrew Rayel:")
print(sorted_ar_years)
Armin van Buuren:
[('1996', 1), ('1999', 1), ('2001', 1), ('2002', 3), ('2003', 4), ('2005', 3), ('2006', 5), ('2007', 1), ('2008', 8), ('2009', 6), ('2010', 13), ('2011', 5), ('2012', 6), ('2013', 17), ('2014', 3), ('2015', 10), ('2016', 6), ('2017', 7), ('2018', 7), ('2019', 16), ('2020', 3)]
Above & Beyond:
[('2003', 2), ('2004', 1), ('2005', 1), ('2006', 2), ('2007', 4), ('2008', 3), ('2009', 2), ('2010', 1), ('2011', 2), ('2012', 2), ('2013', 2), ('2014', 4), ('2015', 2), ('2016', 1), ('2017', 3), ('2018', 6), ('2019', 7), ('2020', 6)]
Aly & Fila:
[('2003', 1), ('2007', 1), ('2008', 2), ('2010', 1), ('2011', 1), ('2012', 3), ('2013', 1), ('2014', 6), ('2015', 2), ('2016', 3), ('2017', 3), ('2018', 3), ('2019', 5), ('2020', 2)]
Ferry Corsten:
[('1995', 1), ('2002', 1), ('2007', 1), ('2008', 1), ('2009', 4), ('2011', 5), ('2012', 1), ('2013', 1), ('2015', 1), ('2016', 1), ('2018', 6), ('2019', 1), ('2020', 1)]
Andrew Rayel:
[('2011', 1), ('2012', 4), ('2013', 4), ('2014', 4), ('2016', 1), ('2017', 2), ('2018', 3), ('2019', 2), ('2020', 4)]

This would look nice in a stacked bar chart, but I couldn't get the data arranged properly to create the chart.

What's the average BPM of tracks in the top 1,000?

total_bpm = 0

for track in top_1000_tracks:
    total_bpm += sp.audio_features(track['track']['uri'])[0]['tempo']

print(total_bpm/1000)
134.03799499999994

Maybe that's not so useful. How does the track BPM vary throughout the top 1,000? With #1,000 on the left, down to #1 on the right.

bpm = []
for track in top_1000_tracks:
    tempo = sp.audio_features(track['track']['uri'])[0]['tempo']
    if tempo < 100 or tempo > 150: # "outliers", details below
        bpm.append(138)
    else:
        bpm.append(sp.audio_features(track['track']['uri'])[0]['tempo'])

x = np.arange(len(top_1000_tracks))   

source = pd.DataFrame({
  'track': x,
  'bpm': np.array(bpm)
})

source['138'] = 138

base = alt.Chart(source).mark_line().encode(
    alt.X('track'),
    alt.Y('bpm', scale=alt.Scale(domain=(100, 150))),
).properties(
    title="ASOT Top 1000 - BPM of track"
)

rule = alt.Chart(source).mark_rule(color='red').encode(
    y='138'
)

base + rule

Not the best way to visualize it, how about a semi-interactive scatter plot? Mouseover for track position and BPM, zoom with the mousewheel. I couldn't figure out how to get track titles and artists in the tooltips.

detail = (
    alt.Chart(source)
    .mark_point()
    .encode(
        x=alt.X(
            "track:T",
        ),
        y=alt.Y(
            "bpm:Q",
            scale=alt.Scale(domain=(100, 150)),
        ),
        color="bpm",
        tooltip=['bpm', 'track']
    )
    .properties(width=600, height=400, title="ASOT Top 1000 -- BPM of track")
).interactive()

detail

There's a few "outliers" that kind of throw off the graph - let's look at the tracks in the top 1,000 with the lowest and highest BPMs.

for position, track in enumerate(top_1000_tracks):
    tempo = sp.audio_features(track['track']['uri'])[0]['tempo']
    if tempo < 125 or tempo > 141: # "outliers"
        track_artist = track['track']['artists'][0]['name']
        for artist in track['track']['artists'][1:]:
            track_artist += " & " + artist['name']
        print(1000 - position, '.', track_artist, '-', track['track']['name'], '-', tempo, 'BPM')
951 . Hilight Tribe & Vini Vici - Free Tibet - Vini Vici Remix - 103.512 BPM
950 . Jody Wisternoff & Sian Evans - The Bridge - Chicane Rework - 124.998 BPM
917 . Vini Vici & Jean Marie & Hilight Tribe - Moyoni - 103.485 BPM
853 . Tinlicker - Sleepwalker - Extended Mix - 123.997 BPM
716 . Armin van Buuren & Alexander Popov - Popcorn - Extended Mix - 94.915 BPM
711 . Parker & Hanson - Gravity - Original Mix - 123.993 BPM
691 . Lost Witness & Lange - Happiness Happening - Lange Remix - 141.85 BPM
654 . Cygnus X - The Orange Theme - Ferry Corsten's Moonman Orange Juice Remix - 144.988 BPM
650 . PRAANA & Matt Fax & HALIENE - Kaleidoscope - Extended Mix - 121.983 BPM
635 . Ilse DeLange - The Great Escape - 98.69 BPM
612 . Monolink & ARTBAT - Return to Oz - ARTBAT Remix - 123.998 BPM
531 . Kyuss - Yeah - 0 BPM
467 . VER:WEST & Tiësto - 5 Seconds Before Sunrise - 121.997 BPM
432 . Will Atkinson - Telescope - Extended Mix - 142.002 BPM
369 . Basic Dawn & Nu NRG - Pure Thrust - NU NRG Remix - 141.981 BPM
303 . Above & Beyond - Is It Love? (1001) - 124.008 BPM
265 . Rank 1 - Such Is Life - Original Version - 141.063 BPM
262 . Armin van Buuren & Candace Sosa - Runaway - Extended Mix - 124.986 BPM
176 . Above & Beyond - Sun In Your Eyes - Original Mix - 183.967 BPM
97 . John O'Callaghan & Bryan Kearney - Exactly - Original Mix - 142.016 BPM
88 . Giuseppe Ottaviani - Linking People - Original Mix - 143.012 BPM

But this is not entirely right, right? Beatport lists Popcorn as 138 BPM. Again, I've done nothing to address any inconsistencies.

Now you!

I've hardly covered the most basic analyses, so I'll leave you with a CSV file of tracks and "audio features" from Spotify so you can run the numbers yourself.

import csv

with open('../data/top-1000.csv', 'w', newline='') as csvfile:
    topreader = csv.writer(csvfile, delimiter=',',
                            quotechar='|', quoting=csv.QUOTE_MINIMAL)
    topreader.writerow(['position', 'artist', 'track', 'year', 'danceability', 'energy', 'key', 'loudness', 'speechiness', 'acousticness', 'instrumentalness', 'liveness', 'valence', 'tempo', 'id', 'uri', 'duration_ms', 'time_signature'])
    for position, track in enumerate(top_1000_tracks):
        # Get track artists
        track_artist = track['track']['artists'][0]['name']
        for artist in track['track']['artists'][1:]:
            track_artist += " & " + artist['name']
        audio_features = sp.audio_features(track['track']['uri'])[0]
        topreader.writerow([1000 - position, track_artist, track['track']['name'], track['track']['album']['release_date'][:4], audio_features['danceability'], audio_features['energy'], audio_features['key'], audio_features['loudness'], audio_features['speechiness'], audio_features['acousticness'], audio_features['instrumentalness'], audio_features['liveness'], audio_features['valence'], audio_features['tempo'], audio_features['id'], audio_features['uri'], audio_features['duration_ms'], audio_features['time_signature']])

The resulting file can be found in https://github.com/ScottBrenner/asot-jupyter/blob/master/csv/top-1000.csv - let me know what you make with it!