【d3.js实战】不同音乐流派的 Spotify 歌曲数据可视化

不同音乐流派的 Spotify 歌曲数据可视化

最近我写了一个关于不同音乐流派的Spotify歌曲数据可视化的页面,使用了d3.js库进行数据的处理和展示。这篇文章将会向大家介绍d3.js库的基本使用和功能,以及为什么选择使用它来进行数据可视化。

题目要求:

  1. 按要求分别输出数据集统计信息:
    1. 统计共有多少种不同音乐流派的歌曲数据;
    2. 统计共有多少位不同的歌手
    3. 统计音乐情感正面度(高于 0.5)有多少首歌曲
  2. 按照流派,将包含歌曲数量前 5 的流派,绘制柱状图,要求动态显示,并 为每个数据柱添加交互,显示本流派的基本信息(共有多少位歌手,以及流行度 高于 80 的歌曲有多少);
  3. 按照歌曲整体的响度,将响度前 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 的流派绘制柱状图,并动态显示每个数据柱的交互信息。具体实现如下:

  1. 遍历原始数据,统计每个流派的歌曲数量、歌手数量以及流行度高于 80 的歌曲数量,并将结果存储在 genre_Obj2 对象中。
  2. genre_Obj2 对象转换为数组 genre_arr,并按歌曲数量降序排序。
  3. 取排序后的数组的前 5 个元素,作为绘制柱状图的数据源。
  4. 使用 D3.js 库绘制柱状图,包括 x 轴和 y 轴的比例尺、坐标轴、矩形元素等。
  5. 为每个矩形元素添加鼠标悬停事件,显示对应的流派信息。
  6. 为每个矩形元素添加鼠标移动事件,更新提示框的位置。
  7. 为每个矩形元素添加鼠标离开事件,隐藏提示框。
  8. 使用过渡动画使柱状图的绘制过程具有动态效果。

绘制折线图

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。

在绘制完折线图后,代码为每个数据点添加鼠标悬停事件。当鼠标悬停在数据点上时,显示一个提示框,显示歌曲名、歌手名、流行度和能量值。同时,更新提示框的位置,使其跟随鼠标移动。当鼠标离开数据点时,隐藏提示框。


百科:牙齿少了并不会太影响进食。

相关推荐
qq_39016177几秒前
防抖函数--应用场景及示例
前端·javascript
枝上棉蛮13 分钟前
GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件
arcgis·gis·数据可视化·数据处理·地理信息系统·gis工具箱·gisbox
John.liu_Test30 分钟前
js下载excel示例demo
前端·javascript·excel
Yaml443 分钟前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事1 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶1 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo1 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v1 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
知孤云出岫1 小时前
web 渗透学习指南——初学者防入狱篇
前端·网络安全·渗透·web
贩卖纯净水.1 小时前
Chrome调试工具(查看CSS属性)
前端·chrome