让数据科学家代替DJ?Python帮你实现

开发 后端
数据科学是一门庞大的学科,它不断地扩展到新的行业,音乐产业就是其中之一。如果把这些应用程序当作一个“黑匣子”,可以观察到它的输入(数据)和输出(产品)。

 [[321520]]

数据科学是一门庞大的学科,它不断地扩展到新的行业,音乐产业就是其中之一。如果把这些应用程序当作一个“黑匣子”,可以观察到它的输入(数据)和输出(产品)。该项目旨在使用Python操作Spotify音乐数据,其范围有两个:

  • 证明API(应用程序编程接口)的存在对于向算法提供超精细数据具有重要意义。
  • 演示简单的统计数据(适当应用时)如何对日常行为进行编码,将其分解为基本要素,并在其基础上构建有价值的产品。

这个故事,最开始是为了调查音乐背后的统计数据,最后发现方程式背后也有“音乐”……

概念

假设一个情景。作为一名数据科学家,笔者工作的数据公司(Data Corp)记录了青年营销部门获得的可观利润,因此公司决定举办聚会犒劳年轻的客户。

在没有DJ的情况下,主管让笔者负责音乐,让派对持续到早上!她给了笔者一些Spotify播放列表,让笔者最终选定并创建一个。然而,忽视现代音乐趋势让笔者遇到一个棘手的问题。

使用Python访问所有播放列表,提取每个曲目的每个音频特征进行统计分析,并将最适合(派对)的曲目包装到最终的播放列表中,如何?当然,这里有一个技术性的问题:一个节拍也听不到!

为了更好地传达结果,假定:

#1:个人Spotify播放列表代表的是公司给笔者的。

#2:该聚会的目标群体是18至30岁的年轻人,这意味着……跳舞!然而,他们中的许多人可能会有家人陪伴,也就是说,一小部分客人会更好地享受到欢快的音乐。

#3:在音频特性中,Spotify只考虑了 danceability, energy, tempo, loudness & valence (见第2节)。那是因为,它们能更好地表达一首曲子是否适合一个舞会。

#4:改进所选播放列表的方法有两种:删除或添加分别被视为“坏”或“好”(涉及一个或多个音频特征)的曲目。笔者只采用后者,以扩大最终的播放列表。

笔者打算按下面的步骤来做:

  • 设置运行代码的环境。
  • 请求Spotify API获取所有相关音乐数据并提供简要说明。
  • 使用Numpy、Pandas和一些附加的Python库执行EDA(探索性数据分析),以数字和可视化的方式探索候选播放列表。
  • 选择-使用两种统计技术优化最合适的播放列表。

一、设置

在本节中,设置所需的环境,以便应用分析技术。如果已经准备好下面列出的任何部分,可以跳过它们。

  • 申请Spotify开发者帐户并创建应用程序。[在此过程中,将创建一个客户ID,并为应用程序提供一个客户机密]。
  • 安装Jupyter Notebook-一个开源的web应用程序,用于创建/共享包含实时代码、方程式、可视化和叙述性文本的文档。
  • 安装Spotipy-用于访问Spotify Web API和请求的轻量级Python库-用于寻址API的Python模块。可以使用CLI(命令行界面)或Jupyter notebook来运行以下命令:
  1. pip install spotipy 
  2.              pipinstall requests 

install.py

  • 导入必要的库:
  1. # Import the libraries 
  2.        import os 
  3.        import pandas as pd 
  4.        import numpy as np 
  5.        import json 
  6.        importmatplotlib.pyplot as plt 
  7.        import seaborn as sns 
  8.        import spotipy 
  9.        importspotipy.util as util 
  10.        fromspotipy.oauth2 importSpotifyClientCredentials 
  • 执行授权代码流:
  1. Declare the credentials 
  2.          cid ='XXXX' 
  3.          secret ='XXXX' 
  4.          redirect_uri='http://localhost:7777/callback' 
  5.          username ='XXXX' 
  6.         # Authorization flow 
  7.          scope ='user-top-read' 
  8.          token = util.prompt_for_user_token(username, scope, client_id=cid,client_secret=secret, redirect_uri=redirect_uri) 
  9.         if token: 
  10.              sp = spotipy.Spotify(auth=token) 
  11.          else
  12.              print("Can'tget token for", username)auth.py 

请注意,与其通过笔记本直接声明凭证,还不如通过相应地设置环境变量,使它们暂时可用:

  1. export SPOTIPY_CLIENT_ID="XXXX" 
  2.                       exportSPOTIPY_CLIENT_SECRET="XXXX" 
  3.               exportSPOTIPY_REDIRECT_URI="http://localhost:7777/callback" 

二、数据解释与获取

Spotify为其开发人员提供了许多表征音轨的音频特征(有关音频特性对象的更全面的解释,请参见此处)。下面列出要使用的特征,并简要说明:

  • Loudness:[-60.0-0db]描述了音轨的整体响度,或者说,声音的质量,这是与体力(振幅)相关的主要心理因素。
  • Energy:[0.0-1.0]描述了对活动和强度的感性测量。充满活力的音轨让人感觉快速、响亮和嘈杂。
  • Valence:[0.0-1.0]描述音轨听起来“积极”的程度。高价意味着更积极的声音(如欢快、欢快等)。
  • Tempo:以BPM为单位描述音轨的总体估计节奏(每分钟节拍),直接从节拍平均的持续时间得出。
  • Danceability:[0.0-1.0]根据音乐元素(节奏、稳定性、节拍强度等)的组合,描述一个轨道是否适合跳舞。低值意味着较少的舞步。

数据提取部分分三步完成:访问用户播放列表;提取每个播放列表的曲目;提取每个曲目的音频特征。对于每个步骤,创建一个函数,实现相应的Spotipy方法。

(a)访问用户的播放列表

  1. deffetch_playlists(sp,username): 
  2.                      ""
  3.                      Returns theuser's playlists. 
  4.                     ""
  5.                      id = [] 
  6.                      name = [] 
  7.                      num_tracks = [] 
  8.                      #Make the API request 
  9.                      playlists = sp.user_playlists(username) 
  10.                      for playlist in playlists['items']: 
  11.                          id.append(playlist['id']) 
  12.                          name.append(playlist['name']) 
  13.                          num_tracks.append(playlist['tracks']['total']) 
  14.                   # Create the final df   
  15.                      df_playlists = pd.DataFrame({"id":id, "name":name"#tracks": num_tracks}) 
  16.                      return df_playlists 
  17.               playlists =fetch_playlists(sp,username) 
  18.                  playlists= playlists[:4].copy() 
  19.                  playlists 

fetch_plst.py

此函数返回一个数据帧,其中包含用户播放列表的id、name和曲目数#tracks。显然,有4个候选播放列表:

让数据科学家代替DJ?Python帮你实现

playlists 数据帧

(b) 获取播放列表的曲目

  1. deffetch_playlist_tracks(sp, username, playlist_id): 
  2.                            ""
  3.                            Returns thetracks for the given playlist. 
  4.                           ""
  5.                            offset =0 
  6.                            tracks = [] 
  7.                            #Make the API request 
  8.                            whileTrue: 
  9.                                content = sp.user_playlist_tracks(username, playlist_id, fields=None, limit=100, offset=offset,market=None) 
  10.                                tracks += content['items'
  11.                                if content['next'] isnotNone: 
  12.                                    offset +=100 
  13.                                else
  14.                                    break 
  15.                                    track_id = [] 
  16.                            track_name = [] 
  17.                            for track in tracks: 
  18.                                track_id.append(track['track']['id']) 
  19.                                track_name.append(track['track']['name']) 
  20.                            #Create the final df 
  21.                            df_playlists_tracks = pd.DataFrame({"track_id":track_id, "track_name": track_name}) 
  22.                            return df_playlists_tracks 

fetch_trcs.py

这个函数以playlist_id作为参数,返回一个数据帧,包括每个曲目的曲目 track_id 和 track_name 。不直接调用它,而是在下面的函数中使用它。

(c) 获取曲目的音频特征

  1. deffetch_audio_features(sp, username, playlist_id): 
  2.                           ""
  3.                           Returns theselected audio features of every track, 
  4.                           for the givenplaylist. 
  5.                          ""
  6.                           # Usethe fetch_playlist_tracks function to fetch all of the tracks 
  7.                           playlist =fetch_playlist_tracks(sp, username, playlist_id) 
  8.                           index =0 
  9.                           audio_features = [] 
  10.                           #Make the API request 
  11.                           while index < playlist.shape[0]: 
  12.                               audio_features += sp.audio_features(playlist.iloc[index:index+50, 0]) 
  13.                               index +=50 
  14.                           #Append the audio features in a list 
  15.                           features_list = [] 
  16.                           for features in audio_features: 
  17.                               features_list.append([features['danceability'], 
  18.                                                     features['energy'],features['tempo'], 
  19.                                                     features['loudness'],features['valence']]) 
  20.                           df_audio_features = pd.DataFrame(features_list,columns=['danceability''energy'
  21.                                              'tempo''loudness''valence']) 
  22.                        # Set the 'tempo' & 'loudness' in the same range withthe rest features 
  23.                           for feature in df_audio_features.columns: 
  24.                               if feature =='tempo'or feature =='loudness'
  25.                                   continue 
  26.                               df_audio_features[feature] =df_audio_features[feature] *100 
  27.                           #Create the final df, using the 'track_id' as index for future reference 
  28.                           df_playlist_audio_features = pd.concat([playlist,df_audio_features], axis=1) 
  29.                           df_playlist_audio_features.set_index('track_id', inplace=Truedrop=True
  30.                           return df_playlist_audio_features 

fetch_aud_ftrs.py

给定 playlist_id 作为参数,此函数返回每个曲目的 track_id, name 和音频特征(danceability, energy, tempo, loudness, valence)。因此,对于4个播放列表中的每一个,创建相应音频特征的数据帧:

  1. df_dinner =fetch_audio_features(sp, username, '37SqXO5bm81JmGCiuhin0L'
  2.                        df_party=fetch_audio_features(sp, username, '2m75Xwwn4YqhwsxHH7Qc9W'
  3.                        df_lounge=fetch_audio_features(sp, username, '6Jbi3Y7ZNNgSrPaZF4DpUp'
  4.                        df_pop=fetch_audio_features(sp, username, '3u2nUYNuI08yUg877JE5FI'

aud_ftrs.py

让数据科学家代替DJ?Python帮你实现

df_dinner 数据框示例

仅仅借助API的力量就获得了所有必要数据的纯编码!

三、EDA

为了减少混乱,这里不包括数据可视化代码,但它可以在GitHub repo上使用。

首先,在一个图中描绘所有播放列表的音频特征,以便容易地感知哪个最适合聚会。

让数据科学家代替DJ?Python帮你实现

音频特征水平条形图

显然,把特征作为整体来看,Party和Pop播放列表取代了另外两个。仔细看看这两个,就能更清楚地了解哪一个占上风…

让数据科学家代替DJ?Python帮你实现

df_party 和df_pop水平横条图

除了danceability特征外,其他特征在派对播放列表中更高。这是一个必须选择和建立,以完善最终的播放列表。

四、播放列表的优化

其主要作用是尽可能增加派对播放列表的音频特征。但是,增加一个特征可能会减少另一个特性,因此必须考虑优先级。就笔者个人而言,根据假设2,danceability(年轻人)应该是主要特征,下一个特征是valence(家庭成员)。

这些变量是定量的,同时也属于比率尺度的测量。因此,箱线图可以有效地描述每个特征的个体分布。这样的图表和描述性统计表(通过pandas.DataFrame.describe方法)可以提供关于每个特定四分位数下的值的比例的良好视觉展示。

下面将详细说明原始的df_party数据框,突出显示danceability和valence平均值、第二(中位数)和第三个四分位数:

让数据科学家代替DJ?Python帮你实现
让数据科学家代替DJ?Python帮你实现

df_party数据的描述性统计和箱线图

竖直的黄线是中位数,而▴符号代表平均数。一般的目标是“推动”每一个特征的分布尽可能地向右,也就是说,沿着播放列表曲目方向增加,以便获得一个更好的“聚会”体验!根据假设4,从df_pop(第二个决赛播放列表)中添加曲目,每次都寻找机会:

  • 将平均值向右移动(增加平均音频特征)
  • 或将中位数移到平均值的右侧(确保至少50%的歌曲高于平均值)
  • 或两者兼而有之

方法一

一个好的出发点是,抽取一个df_pop的样本,并将其添加到主样本(df_party)中,随机的除外。通过使用pandas.DataFrame.sample()函数和weights参数,可以预先配置danceability值越大,就越有可能对相应的行进行采样。这种方法产生的数据框是df_party_exp_I(exp代表expanded)。

  1. # Take a sample from the Pop playlist 
  2.       df_pop_sample_I= df_pop.sample(n=40, weights='danceability',random_state=1) 
  3.       df_pop_sample_I.describe() 
  4.      # Concatenate the original playlist with the sample 
  5.       df_party_exp_I= pd.concat([df_party, df_pop_sample_I]) 
  6.       df_party_exp_I.describe() 

sample_I.py

让数据科学家代替DJ?Python帮你实现
让数据科学家代替DJ?Python帮你实现

df_party_exp_I描述性统计和方框图

  • 主要音频特征danceability增加;平均值上升近0.5,其分布也略有优化。中位数从68.20移到69.30,第三(上)四分位数分别从77.20移到78.90。
  • 但是, valence特征下降了0.61,四分位都没有向右移动。鉴于此,应该寻找进一步的优化机会。

方法二

这一次将利用NumPy布尔索引并过滤Pop播放列表,以便只返回满足指定条件的行。特别是,将danceability和valence特征设置为高于派对播放列表的相应平均值,分别为69.55和51.89。

  1. # Take a sample from the Pop playlist 
  2.        df_pop_sample_II= df_pop[(df_pop['danceability'] >69.55) & (df_pop['valence'] >51.89)].copy() 
  3.       # Concatenate the original playlist with the sample 
  4.        df_party_exp_II= pd.concat([df_party, df_pop_sample_II]) 
  5.        df_party_exp_II.describe() 

sample_II.py

让数据科学家代替DJ?Python帮你实现
让数据科学家代替DJ?Python帮你实现

df_party_exp_II描述性统计和方框图

  • danceability增加更多。这次平均值增加了将近2.17!随着中位数和上四分位数向右移动,沿着该功能的曲目分布也得到了优化,这基本上意味着至少50%的播放列表高于主要声学功能的“新”较高平均值(71.71)。
  • 尽管如此,valence特征下降了4.21,第二和第三个四分位数均高于平均值。

方法三

一个特征的优化并不一定意味着其他特征的优化。为了改善这一缺点,将引入一个方程,其变量是声学特征,参数是赋予它们的权重。既然非常重视danceability特征,那么相应的权重应该更高。最后分数计算如下:

Score =(danceability *30)+(energy *20)+(tempo *20)+(loudness *10)+(valence *20)

为播放列表的每个单曲计算这个分数(创建一个新的列score),然后计算各自的描述性统计。这样,可以更好地评估,丰富df_party,同时在每个特征上实现更统一(根据权重)的优化。

简言之,df_party, df_party_exp_I & df_party_exp_II的平均score分别为7355分、7215分和7416分。很明显,虽然方法一相比原来的播放列表找到了更好的danceability,但它破坏了派对的整体体验(平均score从7355下降到7215)。就方法二而言,平均score提高了近62分。然而,也可以不用这两种方法…

这一次,通过使用新引入的score列,将过滤df_pop数据帧,并获取注意到score高于df_party平均值的行。因此,增加后者!

  1. # Take a sample from the Pop playlist 
  2.       df_pop_sample_III= df_pop[df_pop['score'] > df_party['score'].mean()].copy() 
  3.      # Concatenate the original playlist with the sample 
  4.       df_party_exp_III= pd.concat([df_party, df_pop_sample_III]) 
  5.       df_party_exp_III.describe() 

sample_III.py

让数据科学家代替DJ?Python帮你实现
让数据科学家代替DJ?Python帮你实现

df_party_exp_III描述性统计和方框图

事实上,这次:

  • danceability特征提高了近1.17,valence增加了4.06(平均值的右移)
  • 两种分布都得到改善(中位数移到平均值的右侧)
  • score为122.3,是目前为止最好的!(上下文意味着更高的潜力,播放列表在加权音频特征上更加统一)

作为一个完整的检查,应该一次性描述和比较所有的方块图—这可能看起来有点拥挤。幸运的是,变量的性质(见上文)允许使用KDE(内核密度图)。

让数据科学家代替DJ?Python帮你实现

KDE图

现在非常清楚了,方法三(绿色分布)是最好的,因为它实现了更高的右移。

最后,最终得到的数据帧(df_party_exp_III)包含了最终的音轨。唯一悬而未决的操作是将其转换为真正的播放列表。下面,第一个函数创建最终的播放列表,将其名称作为参数以及描述。另一个,从数据帧迁移轨迹。

请注意,授权流将再次运行,这次将使用不同的作用域(playlist modify public)。只需查看指南就好(https://github.com/makispl/Spotify-Data-Analysis/blob/master/README.md)。

  1. defcreate_playlist(sp,username, playlist_name, playlist_description): 
  2.                         playlists = sp.user_playlist_create(username, playlist_name, description =playlist_description) 
  3.   
  4. create_plst.py 
  5.   
  6. defenrich_playlist(sp,username, playlist_id, playlist_tracks): 
  7.                      index =0 
  8.                      results = [] 
  9.                      while index <len(playlist_tracks): 
  10.                          results += sp.user_playlist_add_tracks(username, playlist_id, tracks =playlist_tracks[index:index +100]) 
  11.                          index +=100 

enrich_plst.py

  1. # Make a temporary list of tracks 
  2.        list_track =df_party_exp_III.index 
  3.       # Create the playlist 
  4.        enrich_playlist(sp,username, '779Uv1K6LcYiiWxblSDjx7', list_track) 

create_plst.py

让数据科学家代替DJ?Python帮你实现

播放列表数据帧

让数据科学家代替DJ?Python帮你实现

Bingo!

结论

到目前为止,已经处理了数百首曲目,检查了它们的音频特征,最后选择了最适合聚会的曲目,只用到了Python。通过这种方式,成功地完成了任务:

  • 演示了简单(描述性)的统计数据和编码(如果适当组合)是如何计算出此类耗时的活动的。
  • “尝到”了拥有可请求的API的重要性,以便提取有意义的数据。

无论是从DJ还是从数据科学家的角度“深入”音乐世界,这无疑都是美妙的……

但是,有效地,当涉及到大量音乐数据集的精确性、敏捷性和彻底处理时,后者可以通过几行代码来指示计算机体面地执行。也就是说,有一件事是必然的:

数据科学已经找到了另一个发展壮大的“市场”,这意味着统计背后确实有“音乐”,达到了节拍背后有数学的水平…

Jupyternotebook已准备好立即运行,让Pandas摇滚吧!

 

责任编辑:华轩 来源: 今日头条
相关推荐

2017-08-04 15:53:10

大数据真伪数据科学家

2020-03-20 14:40:48

数据科学Python学习

2012-12-26 10:51:20

数据科学家

2018-12-24 08:37:44

数据科学家数据模型

2012-12-06 15:36:55

CIO

2019-07-30 11:47:29

数据科学云计算机器学习

2018-02-28 15:03:03

数据科学家数据分析职业

2020-02-12 16:49:49

数据科学技能云计算

2018-10-16 14:37:34

数据科学家数据分析数据科学

2012-12-25 09:58:50

数据科学家大数据

2012-12-26 10:18:47

大数据数据科学家

2012-06-12 09:33:59

2019-09-11 14:34:13

排序算法数据科学

2018-05-04 15:09:25

数据科学面试数据科学家

2018-05-05 07:46:39

数据科学面试大数据

2014-07-03 09:38:19

2012-12-27 09:52:23

数据科学家大数据

2019-08-26 09:47:56

数据科学家数据分析

2015-08-25 13:20:29

数据科学

2015-08-28 09:22:07

数据科学
点赞
收藏

51CTO技术栈公众号