Fix genre graph not displaying
- take top genres/artists for graphs - improve graphs' appearance - update pip packages
This commit is contained in:
@@ -1,137 +1,154 @@
|
||||
function create_genre_graph(data) {
|
||||
// convert strings to nums {{{ //
|
||||
|
||||
data.forEach(function(d) {
|
||||
d.num_songs = +d.num_songs;
|
||||
console.log(d.genre, d.num_songs);
|
||||
let artist_names = Object.keys(d.artists);
|
||||
artist_names.forEach(function(e) {
|
||||
d.artists[e] = +d.artists[e];
|
||||
console.log(e, d.artists[e]);
|
||||
//console.log(e, d.artists[e], d.artists[e] + 1);
|
||||
});
|
||||
// convert strings to nums {{{ //
|
||||
|
||||
data.forEach(function(d) {
|
||||
d.num_songs = +d.num_songs;
|
||||
console.log(d.genre, d.num_songs);
|
||||
let artist_names = Object.keys(d.artists);
|
||||
artist_names.forEach(function(e) {
|
||||
d.artists[e] = +d.artists[e];
|
||||
console.log(e, d.artists[e]);
|
||||
//console.log(e, d.artists[e], d.artists[e] + 1);
|
||||
});
|
||||
});
|
||||
|
||||
// }}} convert strings to nums //
|
||||
|
||||
// domains {{{ //
|
||||
|
||||
data.sort(function(a, b) {
|
||||
return b.num_songs - a.num_songs;
|
||||
});
|
||||
x.domain(data.map(function(d) {
|
||||
return d.genre;
|
||||
}));
|
||||
// y.domain([0, d3.max(data, function(d) { return d.num_songs; }) * 1.25]).nice();
|
||||
y.domain([0, d3.max(data, function(d) {
|
||||
return d.num_songs; // returns the maximum number of songs in the genre
|
||||
}) * 1.6]).nice();
|
||||
|
||||
// }}} domains //
|
||||
|
||||
// setup bar colors {{{ //
|
||||
|
||||
let max_artists = d3.max(data, function(d) {
|
||||
return Object.keys(d.artists).length;
|
||||
});
|
||||
let colorScale = d3.scaleOrdinal().range(randomColor({
|
||||
count: max_artists,
|
||||
luminosity: 'light',
|
||||
// hue: '#3399FF',
|
||||
// hue: '#00ced1',
|
||||
hue: '#0099CC',
|
||||
}));
|
||||
|
||||
// }}} setup bar colors //
|
||||
|
||||
for (let genre_dict of data) {
|
||||
|
||||
// process artist breakdown {{{ //
|
||||
|
||||
// }}} convert strings to nums //
|
||||
|
||||
// domains {{{ //
|
||||
|
||||
data.sort(function(a, b) {
|
||||
return b.num_songs - a.num_songs;
|
||||
});
|
||||
x.domain(data.map(function(d) {
|
||||
return d.genre;
|
||||
}));
|
||||
// y.domain([0, d3.max(data, function(d) { return d.num_songs; }) * 1.25]).nice();
|
||||
y.domain([0, d3.max(data, function(d) {
|
||||
return d.num_songs; // returns the maximum number of songs in the genre
|
||||
})]).nice();
|
||||
|
||||
// }}} domains //
|
||||
|
||||
// setup bar colors {{{ //
|
||||
let keys = Object.keys(genre_dict.artists);
|
||||
let stack = d3.stack()
|
||||
.order(d3.stackOrderDescending)
|
||||
.keys(keys)([genre_dict.artists])
|
||||
// unpack the column
|
||||
.map((d, i) => {
|
||||
return {
|
||||
key: keys[i],
|
||||
data: d[0]
|
||||
}
|
||||
});
|
||||
|
||||
let max_artists = d3.max(data, function(d) {
|
||||
return Object.keys(d.artists).length;
|
||||
});
|
||||
let colorScale = d3.scaleOrdinal().range(randomColor({
|
||||
count: max_artists,
|
||||
luminosity: 'light',
|
||||
}));
|
||||
// }}} process artist breakdown //
|
||||
|
||||
// add bars {{{ //
|
||||
|
||||
// }}} setup bar colors //
|
||||
g.append("g")
|
||||
.selectAll("rect")
|
||||
.data(stack)
|
||||
.enter().append("rect")
|
||||
.attr("x", x(genre_dict.genre))
|
||||
.attr("y", function(d) {
|
||||
return y(d.data[1]);
|
||||
})
|
||||
.attr("height", d => y(d.data[0]) - y(d.data[1]))
|
||||
.attr("width", x.bandwidth())
|
||||
.attr('fill', (d, i) => colorScale(i))
|
||||
.style('font-size', '1.5em')
|
||||
.append('title').text(d => d.key + ': ' + (d.data[1] - d.data[0]).toPrecision(1));
|
||||
|
||||
// }}} add bars //
|
||||
|
||||
for (let genre_dict of data) {
|
||||
|
||||
// process artist breakdown {{{ //
|
||||
|
||||
let keys = Object.keys(genre_dict.artists);
|
||||
let stack = d3.stack()
|
||||
.order(d3.stackOrderDescending)
|
||||
.keys(keys)([genre_dict.artists])
|
||||
// unpack the column
|
||||
.map((d, i) => {
|
||||
return {
|
||||
key: keys[i],
|
||||
data: d[0]
|
||||
}
|
||||
});
|
||||
|
||||
// }}} process artist breakdown //
|
||||
// x-axis {{{ //
|
||||
|
||||
g.append("g")
|
||||
.attr("class", "axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.call(d3.axisBottom(x))
|
||||
.selectAll(".tick text")
|
||||
.style('font-size', '1.5em')
|
||||
.call(wrap, x.bandwidth());
|
||||
|
||||
// }}} x-axis //
|
||||
|
||||
// add bars {{{ //
|
||||
|
||||
g.append("g")
|
||||
.selectAll("rect")
|
||||
.data(stack)
|
||||
.enter().append("rect")
|
||||
.attr("x", x(genre_dict.genre))
|
||||
.attr("y", function(d) {
|
||||
return y(d.data[1]);
|
||||
})
|
||||
.attr("height", d => y(d.data[0]) - y(d.data[1]))
|
||||
.attr("width", x.bandwidth())
|
||||
.attr('fill', (d, i) => colorScale(i))
|
||||
// keep 3 significant figures in the song count label
|
||||
.append('title').text(d => d.key + ': ' + (d.data[1] - d.data[0]).toPrecision(3));
|
||||
|
||||
// }}} add bars //
|
||||
// y-axis {{{ //
|
||||
|
||||
g.append("g")
|
||||
.attr("class", "axis")
|
||||
.call(d3.axisLeft(y).ticks(null, "s"))
|
||||
.append("text")
|
||||
.attr("x", 2)
|
||||
.attr("y", y(y.ticks().pop()) + 0.5)
|
||||
.attr("dy", "0.32em")
|
||||
.attr("fill", "white")
|
||||
.style('font-size', '2em')
|
||||
.attr("text-anchor", "start")
|
||||
.text("Songs");
|
||||
|
||||
// }}} y-axis //
|
||||
|
||||
// x-axis {{{ //
|
||||
|
||||
g.append("g")
|
||||
.attr("class", "axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.call(d3.axisBottom(x))
|
||||
.selectAll(".tick text")
|
||||
.call(wrap, x.bandwidth());
|
||||
|
||||
// }}} x-axis //
|
||||
// title {{{ //
|
||||
|
||||
g.append("text")
|
||||
.attr('x', (width / 2))
|
||||
.attr('y', (margin.top / 2))
|
||||
.attr('fill', "white")
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr("font-weight", "bold")
|
||||
.style('font-size', '2em')
|
||||
.text('Genre Graph (With Artists)');
|
||||
|
||||
// }}} title //
|
||||
|
||||
// y-axis {{{ //
|
||||
|
||||
g.append("g")
|
||||
.attr("class", "axis")
|
||||
.call(d3.axisLeft(y).ticks(null, "s"))
|
||||
.append("text")
|
||||
.attr("x", 2)
|
||||
.attr("y", y(y.ticks().pop()) + 0.5)
|
||||
.attr("dy", "0.32em")
|
||||
.attr("fill", "#000")
|
||||
.attr("font-weight", "bold")
|
||||
.attr("text-anchor", "start")
|
||||
.text("Songs");
|
||||
|
||||
// }}} y-axis //
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wrap text {{{ //
|
||||
// wrapping long labels
|
||||
// https://gist.github.com/guypursey/f47d8cd11a8ff24854305505dbbd8c07#file-index-html
|
||||
function wrap(text, width) {
|
||||
text.each(function() {
|
||||
let text = d3.select(this),
|
||||
words = text.text().split(/\s+/).reverse(),
|
||||
word,
|
||||
line = [],
|
||||
lineNumber = 0,
|
||||
lineHeight = 1.1, // ems
|
||||
y = text.attr("y"),
|
||||
dy = parseFloat(text.attr("dy")),
|
||||
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em")
|
||||
while (word = words.pop()) {
|
||||
line.push(word);
|
||||
tspan.text(line.join(" "));
|
||||
if (tspan.node().getComputedTextLength() > width) {
|
||||
line.pop();
|
||||
tspan.text(line.join(" "));
|
||||
line = [word];
|
||||
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", `${++lineNumber * lineHeight + dy}em`).text(word);
|
||||
}
|
||||
}
|
||||
})
|
||||
text.each(function() {
|
||||
let text = d3.select(this),
|
||||
words = text.text().split(/\s+/).reverse(),
|
||||
word,
|
||||
line = [],
|
||||
lineNumber = 0,
|
||||
lineHeight = 1.1, // ems
|
||||
y = text.attr("y"),
|
||||
dy = parseFloat(text.attr("dy")),
|
||||
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em")
|
||||
while (word = words.pop()) {
|
||||
line.push(word);
|
||||
tspan.text(line.join(" "));
|
||||
if (tspan.node().getComputedTextLength() > width) {
|
||||
line.pop();
|
||||
tspan.text(line.join(" "));
|
||||
line = [word];
|
||||
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", `${++lineNumber * lineHeight + dy}em`).text(word);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// }}} wrap text //
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
{% load static %}
|
||||
{% load sass_tags %}
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Features Graphs</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Features Graphs</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="{% sass_src 'scss/custom.scss' %}">
|
||||
<style>
|
||||
.tick {
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<style>
|
||||
.tick {
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--[if lt IE 7]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
<body>
|
||||
<!--[if lt IE 7]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
@@ -38,10 +38,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://d3js.org/d3.v5.js"></script>
|
||||
<script src="{% static "graphs/scripts/audio_feat_graph.js" %}"></script>
|
||||
<script type="text/javascript">
|
||||
let userSecret = "{{ user_secret }}";
|
||||
<script src="https://d3js.org/d3.v5.js"></script>
|
||||
<script src="{% static "graphs/scripts/audio_feat_graph.js" %}"></script>
|
||||
<script type="text/javascript">
|
||||
let userSecret = "{{ user_secret }}";
|
||||
let graphParams = {
|
||||
"acousticness": {
|
||||
intervalEndPoints: {begin: 0, end: 1.0, step: 0.20},
|
||||
@@ -74,6 +74,6 @@
|
||||
drawAudioFeatGraph(featureKey, params.intervalEndPoints,
|
||||
params.colId, userSecret);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- header {{{ -->
|
||||
|
||||
<!DOC
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!-->
|
||||
{% load static %}
|
||||
{% load sass_tags %}
|
||||
@@ -14,44 +14,46 @@
|
||||
<title>Genre Graph</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- <link rel="stylesheet" href="{% sass_src 'scss/custom.scss' %}"> -->
|
||||
<link rel="stylesheet" href="{% sass_src 'scss/custom.scss' %}">
|
||||
</head>
|
||||
|
||||
<!-- }}} header -->
|
||||
|
||||
<body>
|
||||
<script src="https://d3js.org/d3.v5.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/randomcolor/0.5.2/randomColor.min.js"></script>
|
||||
|
||||
{% load static %}
|
||||
<script src="{% static "graphs/scripts/genre_graph.js" %}"></script>
|
||||
|
||||
<!-- <div class="row">
|
||||
<div class="col-" id="genre-graph"></div>
|
||||
<div class="col-" id="genre-graph"></div>
|
||||
</div> -->
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="genre-column">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="genre-column">
|
||||
</div>
|
||||
</div>
|
||||
<svg id="genre-graph" width="600" height="400"
|
||||
viewBox="0 0 600 400"
|
||||
perserveAspectRatio="xMinYMid">
|
||||
</svg>
|
||||
</div>
|
||||
{% comment %} <svg id="genre-graph" width="600" height="400"
|
||||
viewBox="0 0 600 400"
|
||||
perserveAspectRatio="xMinYMid"> {% endcomment %}
|
||||
<svg id="genre-graph" width="1500" height="900">
|
||||
</svg>
|
||||
<script>
|
||||
let svg = d3.select("svg"),
|
||||
margin = {top: 20, right: 20, bottom: 30, left: 40},
|
||||
width = +svg.attr("width") - margin.left - margin.right,
|
||||
height = +svg.attr("height") - margin.top - margin.bottom,
|
||||
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
let x = d3.scaleBand()
|
||||
.rangeRound([0, width])
|
||||
.paddingInner(0.1)
|
||||
.paddingOuter(0.7)
|
||||
.align(0.1);
|
||||
let y = d3.scaleLinear()
|
||||
.rangeRound([height, 0]);
|
||||
let svg = d3.select("svg"),
|
||||
margin = {top: 20, right: 20, bottom: 30, left: 40},
|
||||
width = +svg.attr("width") - margin.left - margin.right,
|
||||
height = +svg.attr("height") - margin.top - margin.bottom,
|
||||
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
let x = d3.scaleBand()
|
||||
.rangeRound([0, width])
|
||||
.paddingInner(0.1)
|
||||
.paddingOuter(0.7)
|
||||
.align(0.1);
|
||||
let y = d3.scaleLinear()
|
||||
.rangeRound([height, 0]);
|
||||
|
||||
d3.json("{% url "api:get_genre_data" user_secret %}").then(create_genre_graph);
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user