百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

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

toyiye 2024-05-25 20:12 20 浏览 0 评论

荐文计划:

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

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

如果你经常发现好文章并期望与读者分享,

欢迎点击文末“阅读原文”,加入我们!

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

本期荐文:

翻译校对:丁雪 吴怡雯程序验证修改:李小帅

“我相信马塞勒斯·华莱士,我的丈夫,你的老板吩咐你带我出门做我想做的任何事。现在,我想跳舞,我要赢,我想得到那个奖杯,把舞跳好来!”

《黑色追缉令》是我一直以来最喜欢的电影。令人惊奇的故事情节、演员、表演以及导演会让我想要前去影院观看,当别人问起“你看过这部电影吗?”,我可以打破僵局。电影中最具标志性的场景可能是乌玛?瑟曼和约翰?特拉沃尔塔在杰克兔子餐厅的舞池跳扭扭舞的那段。虽然这可能是乌玛?瑟曼最经典的舞蹈场景,但约翰?特拉沃尔塔似乎根本停不下来,在电影《迈克》、《发胶》、《黑色追缉令》、《油脂》、《周末夜狂热》和《都市牛郎》中约翰所饰演的角色总是梳着锃亮的大背头、乌黑的头发、极富本性地跳着舞。

虽然很多人可能会笑约翰在舞池中央跟着迪斯科音乐跳舞的场景,但扪心自问,所有酷酷的舞蹈电影是否都注定是相同的。随着时间流逝我们是否还会被《魅力四射》(Bring it On,美国系列青春校园电影——译者注)和《街舞少年》(Stompthe Yard)中的音乐所感动?如果看一看这些年最流行音乐风格的变化趋势(如下图),大众对流行乐偏好的变化似乎没有迪斯科的节奏那么快。

◆ ◆ ◆

可视化

通过分析Billboard年终榜单中前100首歌曲,我们可以根据每年Billboard上最流行歌曲所代表的音乐风格的份额来量化现代音乐的走向。图中我们可以看出,迪斯科(Disco)只有短短十几年的光辉,从90年代以来饶舌(Rap)和嘻哈(Hip-Hop)音乐风格才持续出现。有趣的是,本世纪初随着历史的重复,饶舌和嘻哈音乐处于巅峰,迪斯科的变动与流行音乐中一些最低份额的流派保持一致。慢摇滚(Soft Rock)和硬摇滚(HardRock)的光景甚至比迪斯科更糟糕,在2005年完全灭绝。相反的是,麦当娜在2005年的复兴单曲继续延续了迪斯科的影响力,在2010年后,我们被火星哥(Bruno Mars)和魔力红(Maroon 5)的歌洗脑。

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

维基百科是一座金矿,里面有列表,列表里面套着列表,甚至被套着的列表里面还套着列表。其中一个列表恰巧是Billboard最热门的100首单曲,它使我们能够很容易地浏览维基百科的数据。在快速查看网址后,我们能够简单地生成页面,从中爬取数据,这样更简单。我们从为程序加载必要的模块和参数开始。

#iPython 内联查看画图并导入必要的包

import numpy as np

import pandas as pd

import seaborn as sns

import pylab as pylab

import matplotlib.pyplot as plt

import requests, cPickle, sys, re, os

from bs4 import BeautifulSoup as bs

import logging

logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s',level=logging.INFO)

requests = requests.Session

# 设置画板大小

pylab.rcParams['figure.figsize'] = 32, 16

接着程序脚本利用我们在网址中找到的模式,尝试从页面中提取所有可能存在的链接。

# 定义一个从维基百科表格中抓取相关信息的函数

如果没有返回NaN

def tryInstance(td, choice):

try:

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

我们创建一个选择标志, #用来决定抓取文本信息还是链接信息

if (choice == 0):

return td.text

elif (choice == 1):

links = [x['href'] for x in td.findAll('a')]

if (len(links) != 0):

return links

else:

return float('NaN')

except:

return float('NaN')

#找到页面的第一个table,尽量抓取所有表格行的信息

pandaTableHeaders = ['year', 'pos', 'song','artists', 'song_links', 'artist_links']

headers = {'User-Agent': 'Mozilla/5.0 (X11;Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) 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'}

def scrapeTable(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 (year in [2006, 2012, 2013]):

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

elif (year in [2011]):

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

#从上面迭遍历程序得到的table中收集每个表格行的信息

for pos, tr in enumerate(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 = pandaTableHeaders

return df

#遍历所有可能的年份,序列化存储,方便以后使用

dfs =pd.DataFrame(pandaTableHeaders).set_index(0).T

for year in xrange(1956, 2016):

print year,

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']

for subject insubjects:

dfs[subject] = float('NaN')

# 与上面的tryInstance函数类似,尽可能抓取更多信息

# 捕获缺失异常,使用NaNs替代缺失值

# 另外,还有一个问题是tables难于管理。其内容可能存在或不存在,可能有错别字

# 或不同的名字。

def extractInfoTable(url):

infoTable =

#捕获表头、表行和页面异常

try:

soup = bs(requests.get(url, headers=headers, cookies=cookies).content)

for tr in soup.find('table').findAll('tr'):

try:

header = tr.find('th').text

if (header == 'Music sample'):

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

break

try:

# 如果表头不是Musicsample,收集”tr”对象中所有可能的信息

trs = tr.findAll('td')

infoTable.append([header,trs])

except:

noTrsFound = True

except:

noHeaderFound = True

except:

noPageFound = True

#如果subjects列表中存在记录,保存HTML字符串形式

infoColumns =

for subject in subjects:

instanceBool = False

for header, info in infoTable:

if (subject in header):

infoColumns.append([subject,str(info)])

instanceBool = True

break

if (not instanceBool):

infoColumns.append([subject,float('NaN')])

#返回所有抓取的信息

return infoColumns

#对数据帧中所有的歌曲使用scraping函数

forsongIndex in xrange(0,dfs.shape[0]):

printsongIndex, dfs.ix[songIndex].year, dfs.ix[songIndex].song

try:

# 获取链接

song_links =['https://en.wikipedia.org' + x for x in dfs.ix[songIndex].song_links]

# 抽取信息

logging.info('extract info')

infoTable =extractInfoTable(song_links[0])

# 存储index和subjectstore信息

for idx, subject in enumerate(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 'NaN link found'

# 保存所有的数据帧信息

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', 'hip hop'],

'metal' : ['metal'],

'grunge' : ['grunge'],

'punk' : ['punk'],

'alt' : ['alternative rock'],

'soft rock' : ['soft rock'],

'hard rock' : ['hard rock'],

}

#加载数据帧并抽取相关的流派

# 添加“dirty”列,名单包括HTML元素

# “ dirty”列包含的错别字、引用等记录都会导致异常发生,但是我们感兴趣的是从

# 混乱的字符串中抽取相关的关键字,通过简单匹配所有的小写实例,计数最后的

#“pop”流派个数

df =cPickle.load(open('full_df.p', 'rb'))

defextractGenre(x):

sx = str(x)

try:

dirtyList = [td.text.replace('\n', '')for td in BeautifulSoup(sx).findAll('td')]

return dirtyList

df['Genre']= df['Genre'].apply(extractGenre)

# 打印df['Genre']

最后我们为每首歌所代表的音乐流派创建标志列,使绘制图片更加容易。

#添加”key”列,如果key是流派字典的键值则为1,否则为0。拷贝数据帧,使

#用.loc[(tuple)]函数以避免切片链警告。

for keyin genreList.keys:

df[key] = 0

dfs =df.copy

# 对于genreList字典中每个流派匹配字符串,如果能匹配,则标志指定列,以便能够在后面输出布尔结果

forgenre in genreList:

ans=0

for idx in xrange(0, df.shape[0]):

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

if (any([x indf.loc[(idx,'Genre')][0].lower for x in genreList[genre]])):

dfs.loc[(idx, genre)] = 1

ans+=1

print genre, ans

sys.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')

for col in gdf.columns:

gdf[col] =gdf[col].divide(gdf['sums']+1e-12)

#返回数据帧并丢弃”sums”列

return gdf.drop('sums', axis=1)

pylab.rcParams['figure.figsize']= 32, 16

gdf =pd.DataFrame

for g ingenreList.keys:

gdf[g] = df.groupby('year')[g].sum

# 自定义打印顺序

gl2 = [

'jazz', 'blues', 'folk', 'soul', 'pop','disco', 'rap', 'soft rock',

'hard rock', '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年的年度热门歌手页面的处理。具体方法是将”for year in xrange(1956, 2016)”程序修改为” foryear in xrange(1956, 1966)”等。您也可以使用我们训练好的模型进行验证,模型文件genre_df.p已按照年份保存到对应目录了,在加载模型文件的目录地址一定不要写错了。

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

#对数据框的每列除以”sums”列,添加精度1e-12,排除分母为零的情况

#返回数据框并丢弃“sums”列

#对数据框重新排序并对求平均

维基百科真是个宝,里面有很多可以挖掘的内容,欢迎读者朋友们也试试,给我们投投稿,谢谢!

大数据文摘译文团队

后台回复“志愿者”了解如何加入

震撼可视化,4分钟看完世界5500年版图演变(视频)

数学即音乐,统计即文学

【案例】网易音乐的个性化推荐

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码