如何利用维基百科的数据可视化当代音乐史.docx

上传人:b****9 文档编号:25244864 上传时间:2023-06-06 格式:DOCX 页数:6 大小:20.47KB
下载 相关 举报
如何利用维基百科的数据可视化当代音乐史.docx_第1页
第1页 / 共6页
如何利用维基百科的数据可视化当代音乐史.docx_第2页
第2页 / 共6页
如何利用维基百科的数据可视化当代音乐史.docx_第3页
第3页 / 共6页
如何利用维基百科的数据可视化当代音乐史.docx_第4页
第4页 / 共6页
如何利用维基百科的数据可视化当代音乐史.docx_第5页
第5页 / 共6页
点击查看更多>>
下载资源
资源描述

如何利用维基百科的数据可视化当代音乐史.docx

《如何利用维基百科的数据可视化当代音乐史.docx》由会员分享,可在线阅读,更多相关《如何利用维基百科的数据可视化当代音乐史.docx(6页珍藏版)》请在冰豆网上搜索。

如何利用维基百科的数据可视化当代音乐史.docx

如何利用维基百科的数据可视化当代音乐史

如何利用维基百科的数据可视化当代音乐史

荐文计划:

你的工作是否和数据有关?

又或者是否大数据爱好者?

如果你经常发现好文章并期望与读者分享,欢迎点击文末“阅读原文”,加入我们!

荐文一旦采纳,我们会在文章开头致谢并宣传。

本期荐文:

翻译校对:

丁雪吴怡雯程序验证修改:

李小帅

“我相信马塞勒斯·华莱士,我的丈夫,你的老板吩咐你带我出门做我想做的任何事。

现在,我想跳舞,我要赢,我想得到那个奖杯,把舞跳好来!

《黑色追缉令》是我一直以来最喜欢的电影。

令人惊奇的故事情节、演员、表演以及导演会让我想要前去影院观看,当别人问起“你看过这部电影吗?

”,我可以打破僵局。

电影中最具标志性的场景可能是乌玛·瑟曼和约翰·特拉沃尔塔在杰克兔子餐厅的舞池跳扭扭舞的那段。

虽然这可能是乌玛·瑟曼最经典的舞蹈场景,但约翰·特拉沃尔塔似乎根本停不下来,在电影《迈克》、《发胶》、《黑色追缉令》、《油脂》、《周末夜狂热》和《都市牛郎》中约翰所饰演的角色总是梳着锃亮的大背头、乌黑的头发、极富本性地跳着舞。

虽然很多人可能会笑约翰在舞池中央跟着迪斯科音乐跳舞的场景,但扪心自问,所有酷酷的舞蹈电影是否都注定是相同的。

随着时间流逝我们是否还会被《魅力四射》(BringitOn,美国系列青春校园电影——译者注)和《街舞少年》(StomptheYard)中的音乐所感动?

如果看一看这些年最流行音乐风格的变化趋势(如下图),大众对流行乐偏好的变化似乎没有迪斯科的节奏那么快。

可视化通过分析Billboard年终榜单中前100首歌曲,我们可以根据每年Billboard上最流行歌曲所代表的音乐风格的份额来量化现代音乐的走向。

图中我们可以看出,迪斯科(Disco)只有短短十几年的光辉,从90年代以来饶舌(Rap)和嘻哈(Hip-Hop)音乐风格才持续出现。

有趣的是,本世纪初随着历史的重复,饶舌和嘻哈音乐处于巅峰,迪斯科的变动与流行音乐中一些最低份额的流派保持一致。

慢摇滚(SoftRock)和硬摇滚(HardRock)的光景甚至比迪斯科更糟糕,在2005年完全灭绝。

相反的是,麦当娜在2005年的复兴单曲继续延续了迪斯科的影响力,在2010年后,我们被火星哥(BrunoMars)和魔力红(Maroon5)的歌洗脑。

这一可视化视图是如何绘制而成的?

维基百科是一座金矿,里面有列表,列表里面套着列表,甚至被套着的列表里面还套着列表。

其中一个列表恰巧是Billboard最热门的100首单曲,它使我们能够很容易地浏览维基百科的数据。

在快速查看网址后,我们能够简单地生成页面,从中爬取数据,这样更简单。

我们从为程序加载必要的模块和参数开始。

#iPython内联查看画图并导入必要的包importnumpyasnpimportpandasaspdimportseabornassnsimportpylabaspylabimportmatplotlib.pyplotaspltimportrequests,cPickle,sys,re,osfrombs4importBeautifulSoupasbsimportlogginglogging.basicConfig(format='%(asctime)s:

%(levelname)s:

%(message)s',level=logging.INFO)requests=requests.Session()#设置画板大小pylab.rcParams['figure.figsize']=32,16接着程序脚本利用我们在网址中找到的模式,尝试从页面中提取所有可能存在的链接。

#定义一个从维基百科表格中抓取相关信息的函数,如果没有返回NaNdeftryInstance(td,choice):

try:

#歌曲只有一个维基百科链接,但是歌手可能有许多链接。

我们创建一个选择标志,#用来决定抓取文本信息还是链接信息if(choice==0):

returntd.textelif(choice==1):

links=[x['href']forxintd.findAll('a')]if(len(links)!

=0):

returnlinkselse:

returnfloat('NaN')except:

returnfloat('NaN')#找到页面的第一个table,尽量抓取所有表格行的信息pandaTableHeaders=['year','pos','song','artists','song_links','artist_links']headers={'User-Agent':

'Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/48.0.2564.116Safari/537.36'}cookies={'cookie':

'CP=H2;WMF-Last-Access=16-Apr-2016;GeoIP=CN:

01:

Xuancheng:

30.95:

118.76:

v4;enwikimwuser-sessionId=588324132f65192a'}defscrapeTable(year):

#创建url路径,用BeautifulSoup解析页面内容,创建列表用来存储表数据url='https:

//en.wikipedia.org/wiki/Billboard_Year-End_Hot_100_singles_of_'+str(year)soup=bs(requests.get(url,headers=headers,cookies=cookies).content)table=[]#由于文本格式的不同,我们针对4种特例使用不同的code来创建临时souptable变量souptable=soup.find('table')if(yearin[2006,2012,2013]):

souptable=soup.findAll('table')[1]elif(yearin[2011]):

souptable=soup.findAll('table')[4]

#从上面迭遍历程序得到的table中收集每个表格行的信息forpos,trinenumerate(souptable.findAll('tr')):

tds=tr.findAll('td')if(len(tds)>0):

toAppend=[year,pos,tryInstance(tds[-2],0),tryInstance(tds[-1],0),tryInstance(tds[-2],1),tryInstance(tds[-1],1)]table.append(toAppend)#创建并返回表数据的数据框形式df=pd.DataFrame(table)df.columns=pandaTableHeadersreturndf#遍历所有可能的年份,序列化存储,方便以后使用dfs=pd.DataFrame(pandaTableHeaders).set_index(0).Tforyearinxrange(1956,2016):

printyear,dfs=dfs.append(scrapeTable(year))cPickle.dump(dfs.reset_index().drop('index',axis=1),open('wikipediaScrape.p','wb'))借助存储在数据帧中的所有链接,我们可以加载每个维基百科页面,并从每一页右上角信息表中提取信息。

不幸的是,当所有这些信息表的长度不同,有不同的HTML嵌套和不完整数据时,这些数据会变得特别混杂(竟然没有人将Gorillaz音乐进行归类?

)。

为了解决这一问题,我们在代码中查找表对象,并将其作为字符串保存并在之后的分析进行加载。

这样做的优点是加倍的,它可以让我们从一次运行中收集所有必要的信息;同时,也帮助我们从用户的定义中对音乐流派关键词进行分类。

#从wikipediaScrape.p文件中加载数据框,创建新的列,边抓取信息边填充dfs=cPickle.load(open('wikipediaScrape.p','rb'))subjects=['Genre','Length','Producer','Label','Format','Released','B-side']forsubjectinsubjects:

dfs[subject]=float('NaN')#与上面的tryInstance函数类似,尽可能抓取更多信息#捕获缺失异常,使用NaNs替代缺失值#另外,还有一个问题是tables难于管理。

其内容可能存在或不存在,可能有错别字#或不同的名字。

defextractInfoTable(url):

infoTable=[]#捕获表头、表行和页面异常try:

soup=bs(requests.get(url,headers=headers,cookies=cookies).content)fortrinsoup.find('table').findAll('tr'):

try:

header=tr.find('th').textif(header=='Musicsample'):

#Musicsample表示信息表的结束,如果满足条件中断循环以节省时间breaktry:

#如果表头不是Musicsample,收集”tr”对象中所有可能的信息trs=tr.findAll('td')infoTable.append([header,trs])except:

noTrsFound=Trueexcept:

noHeaderFound=Trueexcept:

noPageFound=True#如果subjects列表中存在记录,保存HTML字符串形式infoColumns=[]forsubjectinsubjects:

instanceBool=Falseforheader,infoininfoTable:

if(subjectinheader):

infoColumns.append([subject,str(info)])instanceBool=Truebreakif(notinstanceBool):

infoColumns.append([subject,float('NaN')])#返回所有抓取的信息returninfoColumns#对数据帧中所有的歌曲使用scraping函数forsongIndexinxrange(0,dfs.shape[0]):

printsongIndex,dfs.ix[songIndex].year,dfs.ix[songIndex].songtry:

#获取链接song_links=['https:

//en.wikipedia.org'+xforxindfs.ix[songIndex].song_links]#抽取信息logging.info('extractinfo')infoTable=extractInfoTable(song_links[0])#存储index和subjectstore信息foridx,subjectinenumerate(subjects):

dfs.loc[:

(subject)].ix[songIndex]=str(infoTable[idx][1])#每100首歌曲序列化保存if(songIndex%100==0):

cPickle.dump(dfs.reset_index().drop('index',axis=1),open('full_df.p','wb'))except(TypeError):

print'NaNlinkfound'#保存所有的数据帧信息cPickle.dump(dfs.reset_index().drop('index',axis=1),open('full_df.p','wb'))现在,我们开始对所有HTML字符串进行分析。

当音乐流派可以被识别时,我们就可以抽取关键词列表,之后将它们分入“脏列表”(脏,表示数据还未被清洗——译者注)。

这一列表充满了错别字、名称不统一的名词、引用等等。

#创建流派字典,比如,对于“folk”和“country”范围的分析则认为是相同的音乐流#派genreList={'electronic':

['electronic'],'latin':

['latin'],'reggae':

['reggae'],'pop':

['pop'],'dance':

['dance'],'disco':

['disco','funk'],'folk':

['folk','country'],'r&b':

['r&b'],'blues':

['blues'],'jazz':

['jazz'],'soul':

['soul'],'rap':

['rap','hiphop'],'metal':

['metal'],'grunge':

['grunge'],'punk':

['punk'],'alt':

['alternativerock'],'softrock':

['softrock'],'hardrock':

['hardrock'],}#加载数据帧并抽取相关的流派#添加“dirty”列,名单包括HTML元素#“dirty”列包含的错别字、引用等记录都会导致异常发生,但是我们感兴趣的是从#混乱的字符串中抽取相关的关键字,通过简单匹配所有的小写实例,计数最后的#“pop”流派个数df=cPickle.load(open('full_df.p','rb'))defextractGenre(x):

sx=str(x)try:

dirtyList=[td.text.replace('\n','')fortdinBeautifulSoup(sx).findAll('td')]returndirtyListexcept:

returnfloat('NaN')df['Genre']=df['Genre'].apply(extractGenre)#打印df['Genre']最后我们为每首歌所代表的音乐流派创建标志列,使绘制图片更加容易。

#添加”key”列,如果key是流派字典的键值则为1,否则为0。

拷贝数据帧,使#用.loc[(tuple)]函数以避免切片链警告。

forkeyingenreList.keys():

df[key]=0dfs=df.copy()#对于genreList字典中每个流派匹配字符串,如果能匹配,则标志指定列,以便能够在后面输出布尔结果forgenreingenreList:

ans=0foridxinxrange(0,df.shape[0]):

if(len(df.loc[(idx,'Genre')])>0):

if(any([xindf.loc[(idx,'Genre')][0].lower()forxingenreList[genre]])):

dfs.loc[(idx,genre)]=1ans+=1printgenre,anssys.stdout.flush()cPickle.dump(dfs,open('genre_df.p','wb'))微调变量后导出数据df=cPickle.load(open('genre_df.p','rb'))defaverageAllRows(gdf):

#添加”sums”列gdf['sums']=gdf.sum(axis=1)#对数据帧的每列除以”sums”列,添加精度1e-12,排除分母为零的情况logging.info('averageAllRows')forcolingdf.columns:

gdf[col]=gdf[col].divide(gdf['sums']+1e-12)#返回数据帧并丢弃”sums”列returngdf.drop('sums',axis=1)pylab.rcParams['figure.figsize']=32,16gdf=pd.DataFrame()forgingenreList.keys():

gdf[g]=df.groupby('year')[g].sum()#自定义打印顺序gl2=['jazz','blues','folk','soul','pop','disco','rap','softrock','hardrock','dance','r&b','alt','latin','reggae','electronic','punk','grunge','metal',]#对数据帧重新排序并对所有行求平均gdf=gdf[gl2]gdf=averageAllRows(gdf)#创建百分比条形图ax=gdf.plot(kind='bar',width=1,stacked=True,legend=False,cmap='Paired',linewidth=0.1)ax.set_ylim(0,1)ax.legend(loc='centerleft',bbox_to_anchor=(1,0.5))locs,labels=plt.xticks()plt.setp(labels,rotation=90)plt.show()最后的输出编后语

由于程序是对1956年-2016年期间的Wiki年度热门歌手页面的爬取,处理过程很耗时,因此,我们将1956-2016时间段分成了6部分,每部分包含了跨度为10年的年度热门歌手页面的处理。

具体方法是将”foryearinxrange(1956,2016)”程序修改为”foryearinxrange(1956,1966)”等。

您也可以使用我们训练好的模型进行验证,模型文件genre_df.p已按照年份保存到对应目录了,在加载模型文件的目录地址一定不要写错了。

df=cPickle.load(open('./06_16/genre_df.p','rb'))defaverageAllRows(gdf):

#添加”sums”列gdf['sums']=gdf.sum(axis=1)#对数据框的每列除以”sums”列,添加精度1e-12,排除分母为零的情况logging.info('averageAllRows')forcolingdf.columns:

gdf[col]=gdf[col].divide(gdf['sums']+1e-12)#返回数据框并丢弃“sums”列returngdf.drop('sums',axis=1)pylab.rcParams['figure.figsize']=32,16gdf=pd.DataFrame()forgingenreList.keys():

gdf[g]=df.groupby('year')[g].sum()#自定义打印顺序gl2=['jazz','blues','folk','soul','pop','disco','rap','softrock','hardrock','dance','r&b','alt','latin','reggae','electronic','punk','grunge','metal',]#对数据框重新排序并对求平均gdf=gdf[gl2]gdf=averageAllRows(gdf)#创建百分比条形图ax=gdf.plot(kind='bar',width=1,stacked=True,legend=False,cmap='Paired',linewidth=0.1)ax.set_ylim(0,1)ax.legend(loc='centerleft',bbox_to_anchor=(1,0.5))locs,labels=plt.xticks()plt.setp(labels,rotation=90)plt.show()

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 管理学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1