Introduction

Previously on "How has A State of Trance changed over time?" we poked the Spotify Web API and returned with every episode of A State of Trance. In this post, we'll examine how the show's BPM has changed over time.

On Averages

First, some housekeeping items.

In the "Methodology" post we learned Spotify's catalogue of A State of Trance, while it seems to contain every episode, does not have every track in some episodes.

Due to the nature of averages, this will cause our calculations to be skewed somewhat. Filling in the blanks is left as an exercise for the reader.

Calculating

Let's find the average BPM of each episode and graph it over time.

We'll ask Spotify for the BPM of every track in an episode, add it all up, and divide by the number of tracks - removing the voiceovers and interludes of course! For the first 10 episodes, this looks like:

episode_avg_bpm = []

for episode in episodes[:10]:
    try:
        episode_bpm = 0
        tracks_counted = 0
        for track in sp.album_tracks(episode['uri'])['items']:
            if "a atate of trance" in track['name'].lower() or "- interview" in track['name'].lower():
                continue
            else:
                episode_bpm += sp.audio_features(track['uri'])[0]['tempo']
                tracks_counted += 1
        avg = episode_bpm/tracks_counted
        print(episode['name'], avg)
        episode_avg_bpm.append(avg)
    except:
        pass
A State Of Trance Episode 000 135.77875
A State Of Trance Episode 001 138.86957142857142
A State Of Trance Episode 002 135.95900000000003
A State Of Trance Episode 003 135.295
A State Of Trance Episode 004 137.3078
A State Of Trance Episode 005 137.122875
A State Of Trance Episode 006 136.12875
A State Of Trance Episode 007 137.459125
A State Of Trance Episode 008 136.409
A State Of Trance Episode 009 138.58033333333333

Rinse and repeat for 950+ episodes. Grab a tea or coffee, this could take a bit!

episode_avg_bpm = []

for episode in episodes:
    try:
        episode_bpm = 0
        tracks_counted = 0
        for track in sp.album_tracks(episode['uri'])['items']:
            if "a state of trance" in track['name'].lower() or "- interview" in track['name'].lower():
                continue
            else:
                episode_bpm += sp.audio_features(track['uri'])[0]['tempo']
                tracks_counted += 1
        avg = episode_bpm/tracks_counted
        episode_avg_bpm.append(avg)
    except:
        pass

Instead of printing this, let's throw it into a quick Altair graph:

x = np.arange(len(episode_avg_bpm))   

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

alt.Chart(source).mark_line().encode(
    alt.X('episode'),
    alt.Y('avg bpm', scale=alt.Scale(domain=(120, 150))),
).properties(
    title="A State of Trance - Average BPM of episode"
)

Episode 057 tops the charts at an impressive 148 BPM, why? Looking at the tracklist ..

"""
Artist: Armin van Buuren ASOT Radio
Album: A State Of Trance Episode 057
Album link: https://open.spotify.com/album/56qM5Y21wbvCW9l5GiAiaV
Album ID: 56qM5Y21wbvCW9l5GiAiaV
"""

episode_bpm = 0
tracks_counted = 0
for track in sp.album_tracks('56qM5Y21wbvCW9l5GiAiaV')['items']:
    if "a state of trance" in track['name'].lower() or "- interview" in track['name'].lower():
        continue
    else:
        print(track['artists'][0]['name'], '-', track['name'], sp.audio_features(track['uri'])[0]['tempo'])
        episode_bpm += sp.audio_features(track['uri'])[0]['tempo']
        tracks_counted += 1
print("Episode average: ", episode_bpm/tracks_counted)
Pulser - Cloudwalking [ASOT 057] - Beatpusher Remix 141.61
Gouryella - Ligaya [ASOT 057] - Original Mix 141.784
Tiësto - Obsession [ASOT 057] - Original Mix 136.377
UMEK - Gatex [ASOT 057] - Original Mix 184.848
Cygnus X - Positron [ASOT 057] - Marco V Remix 139.264
Episode average:  148.7766

UMEK - Gatex (Original Mix) clocks in at a whopping 184 BPM, drastically raising the average of this episode. Beatport reports it at 136, something's not right..

Results

Finally, for fun, we can draw a line at 138 BPM to compare.

x = np.arange(len(episode_avg_bpm))

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

source['138'] = 138

base = alt.Chart(source).mark_line().encode(
    alt.X('episode'),
    alt.Y('avg bpm', scale=alt.Scale(domain=(120, 150))),
).properties(
    title="A State of Trance - Average BPM of episode"
)

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

base + rule

And there we have it! There's plenty more to investigate beyond BPM, stay tuned..