不同音乐流派的 Spotify 歌曲数据可视化
最近我写了一个关于不同音乐流派的Spotify歌曲数据可视化的页面,使用了d3.js库进行数据的处理和展示。这篇文章将会向大家介绍d3.js库的基本使用和功能,以及为什么选择使用它来进行数据可视化。
题目要求:
- 按要求分别输出数据集统计信息:
- 统计共有多少种不同音乐流派的歌曲数据;
- 统计共有多少位不同的歌手
- 统计音乐情感正面度(高于 0.5)有多少首歌曲
- 按照流派,将包含歌曲数量前 5 的流派,绘制柱状图,要求动态显示,并 为每个数据柱添加交互,显示本流派的基本信息(共有多少位歌手,以及流行度 高于 80 的歌曲有多少);
- 按照歌曲整体的响度,将响度前 20 的歌曲,绘制折线图,并为每个数据点 添加交互,显示该歌曲的歌手名,流行度和能量值。
D3.js是什么?
D3.js是一个基于JavaScript的库,可以帮助我们利用数据进行文档操作,同时也是一种数据可视化的工具。D3.js能够使数据变得更加生动、直观,让人们更加容易地理解和分析数据。
D3.js的特点
- 灵活性:D3允许我们对数据进行任意操作,可以帮助我们快速完成复杂的数据操作和可视化需求。
- 可重用性:D3的代码非常模块化,可以重复使用代码中的某个部分,同时也可以把代码嵌入到其他项目中。
- 交互性:D3.js可以帮助我们实现与用户的交互,让用户可以自由地选择和操作数据。
数据来源
该页面展示的数据来自Spotify API,其中包含不同音乐流派的歌曲数据,包括歌曲数量、歌手数量和情感正面度高于0.5的歌曲数量等。
可视化效果
页面中使用了两个d3.js的可视化效果,分别是:
- 柱状图:展示了歌曲数量前5的音乐流派;
- 折线图:展示了响度前20的歌曲。
通过这两个可视化效果,我们可以更加直观地了解不同音乐流派的歌曲数据。同时,我们也可以根据自己的需求来选择不同的可视化方式,实现更好的数据展示效果。
统计信息
js
var tip = document.querySelector('.tip'); // 选择页面上class为'tip'的元素
var dis1 = document.querySelector('.dis1'); // 选择页面上class为'dis1'的元素
var dis2 = document.querySelector('.dis2'); // 选择页面上class为'dis2'的元素
var dis3 = document.querySelector('.dis3'); // 选择页面上class为'dis3'的元素
var bar = d3.select('#bar'); // 选择id为'bar'的元素
var line = d3.select('#line'); // 选择id为'line'的元素
var width = 600; // 设置图表的宽度为600像素
var height = 300; // 设置图表的高度为300像素
var margin = { top: 20, right: 20, bottom: 30, left: 50 }; // 设置图表的边距,包括上、右、下、左四个方向的距离
通过document.querySelector方法选择了页面上具有指定类名或ID的元素,并将它们分别赋值给变量tip、dis1、dis2、dis3。然后,使用d3.select方法选择了具有指定ID的元素,并将它们分别赋值给变量bar和line。接下来,设置了图表的宽度、高度和边距。
js
var data = [];
var track_Obj = {};
for (var i = 0; i < sdata.length; i++) {
var item = sdata[i];
if (!track_Obj[item.track_id]) {
track_Obj[item.track_id] = true;
data.push(item);
}
}
// 统计共有多少种不同音乐流派的歌曲数据
var genre_Obj = {};
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (!genre_Obj[item.track_genre]) {
genre_Obj[item.track_genre] = true;
}
}
var genre_num = Object.keys(genre_Obj).length;
dis1.innerHTML = '共有 ' + genre_num + ' 种不同音乐流派的歌曲数据。';
var artist_Obj = {};
for (var i = 0; i < data.length; i++) {
var item = data[i];
var artists = item.artists.split(';');
for (var j = 0; j < artists.length; j++) {
var artist = artists[j];
if (!artist_Obj[artist]) {
artist_Obj[artist] = true;
}
}
}
var artist_num = Object.keys(artist_Obj).length;
dis2.innerHTML = '共有 ' + artist_num + ' 位不同的歌手。';
//3 统计音乐情感正面度(高于 0.5)有多少首歌曲
var positive_num = 0;
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (item.valence > 0.5) {
positive_num++;
}
}
dis3.innerHTML = '共有 ' + positive_num + ' 首歌曲情感正面度高于 0.5。';
首先,通过遍历原始数据(sdata),将不重复的音乐曲目添加到一个新的数组(data)中。然后,使用一个对象(genre_Obj)来统计不同音乐流派的数量,最后将结果显示在页面上的dis1元素中。
接下来,再次遍历data数组,将每个曲目的歌手信息分割成数组(artists),并使用另一个对象(artist_Obj)来统计不同歌手的数量。最后,将结果显示在页面上的dis2元素中。
最后,遍历data数组,统计情感正面度高于0.5的歌曲数量(positive_num),并将结果显示在页面上的dip3元素中。
绘制柱状图
ini
var genre_Obj2 = {};
for (var i = 0; i < data.length; i++) {
var item = data[i];
if (!genre_Obj2[item.track_genre]) {
genre_Obj2[item.track_genre] = {
count: 1,
artists: {},
popularity: 0
};
} else {
genre_Obj2[item.track_genre].count++;
}
var artists = item.artists.split(';');
for (var j = 0; j < artists.length; j++) {
var artist = artists[j];
if (!genre_Obj2[item.track_genre].artists[artist]) {
genre_Obj2[item.track_genre].artists[artist] = true;
}
}
if (item.popularity > 80) {
genre_Obj2[item.track_genre].popularity++;
}
}
var genre_arr = [];
for (var key in genre_Obj2) {
var item = genre_Obj2[key];
item.genre = key;
item.artists = Object.keys(item.artists).length;
genre_arr.push(item);
}
genre_arr.sort(function (a, b) {
return b.count - a.count;
});
var genre_arr5 = genre_arr.slice(0, 5);
var x = d3.scaleBand()
.domain(genre_arr5.map(function (d) {
return d.genre;
}))
.rangeRound([margin.left, width - margin.right])
.padding(0.5);
var y = d3.scaleLinear()
.domain([d3.min(genre_arr5, function (d) {
return d.count;
}) - 1, d3.max(genre_arr5, function (d) {
return d.count;
})]).nice()
.rangeRound([height - margin.bottom, margin.top]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
按照流派,将包含歌曲数量前 5 的流派绘制柱状图,并动态显示每个数据柱的交互信息。具体实现如下:
- 遍历原始数据,统计每个流派的歌曲数量、歌手数量以及流行度高于 80 的歌曲数量,并将结果存储在
genre_Obj2
对象中。 - 将
genre_Obj2
对象转换为数组genre_arr
,并按歌曲数量降序排序。 - 取排序后的数组的前 5 个元素,作为绘制柱状图的数据源。
- 使用 D3.js 库绘制柱状图,包括 x 轴和 y 轴的比例尺、坐标轴、矩形元素等。
- 为每个矩形元素添加鼠标悬停事件,显示对应的流派信息。
- 为每个矩形元素添加鼠标移动事件,更新提示框的位置。
- 为每个矩形元素添加鼠标离开事件,隐藏提示框。
- 使用过渡动画使柱状图的绘制过程具有动态效果。
绘制折线图
js
var loudness_arr = [];
for (var i = 0; i < data.length; i++) {
var item = data[i];
loudness_arr.push({
track_name: item.track_name,
artists: item.artists,
popularity: item.popularity,
loudness: item.loudness
});
}
loudness_arr.sort(function (a, b) {
return b.loudness - a.loudness;
});
var loudness_arr20 = loudness_arr.slice(0, 20);
console.log(loudness_arr20);
//4 绘制折线图
var x2 = d3.scaleBand()
.domain(d3.range(loudness_arr20.length))
.rangeRound([margin.left, width - margin.right])
.padding(0.5)
// .paddingInner(1);
var y2 = d3.scaleLinear()
.domain([0, d3.max(loudness_arr20, function (d) {
return d.loudness;
})]).nice()
.rangeRound([height - margin.bottom, margin.top]);
var xAxis2 = d3.axisBottom(x2).tickFormat((d,i)=> i+ 1);
var yAxis2 = d3.axisLeft(y2);
line.append('g')
.attr('transform', 'translate(0,' + (height - margin.bottom) + ')')
.call(xAxis2);
line.append('g')
.attr('transform', 'translate(' + margin.left + ',0)')
.call(yAxis2);
按照歌曲整体的响度,将响度前 20 的歌曲绘制折线图,并为每个数据点添加交互,显示该歌曲的歌手名、流行度和能量值。
首先,代码遍历原始数据,将每首歌曲的曲目名、歌手名、流行度和响度存储在一个对象中,并将这些对象添加到 loudness_arr 数组中。然后,根据响度对 loudness_arr 数组进行降序排序,并取前 20 个元素作为 loudness_arr20。
接下来,代码使用 D3.js 库绘制折线图。首先定义 x 轴的比例尺,将索引映射到宽度上,并设置间距为 0.5。然后定义 y 轴的比例尺,将响度映射到高度上,并设置 nice 方法以自动调整刻度。接着,创建 x 轴和 y 轴的坐标轴,并添加到 line 元素中。最后,使用 d3.line() 函数创建一个路径生成器,并设置 x 和 y 的映射函数。将 loudness_arr20 作为数据传递给路径生成器,并设置填充颜色为 none,描边颜色为 #76D565,描边宽度为 1.5。
在绘制完折线图后,代码为每个数据点添加鼠标悬停事件。当鼠标悬停在数据点上时,显示一个提示框,显示歌曲名、歌手名、流行度和能量值。同时,更新提示框的位置,使其跟随鼠标移动。当鼠标离开数据点时,隐藏提示框。
百科:牙齿少了并不会太影响进食。