Feature Correlation Matrix
Plot.plot({
marginBottom:70,
marginLeft:100,
//adjust plot dimensions
//adjust plot labels
label: null, //specific for x and y axis labels
style: {fontFamily: "Roboto"},
//customize x and y axis
y: {tickSize:0},
x: {tickSize:0},
//customize legend
color: { scheme: "PuOr", pivot: 0, legend: true, label: "Correlation" },
marks: [
Plot.cell(correlations, { x: "a", y: "b", fill: "correlation" }),
Plot.text(correlations, {
x: "a",
y: "b",
fontSize: 15,
//map using functions to do something additional with data
text: d => d.correlation.toFixed(2), // round corr values to 2nd decimal
fill: d => (Math.abs(d.correlation) > 0.6 ? "white" : "black") //if then function for text color
})
]
})
Feature Cross Analysis
viewof filters = (
Inputs.form({
selectX : Inputs.select(features, {label:"Select X", value:"energy"}),
selectY : Inputs.select(features, {label:"Select Y", value:"acousticness"}),
},
{template})
)
Plot.plot({
grid: true,
marginBottom:20,
marks: [
Plot.ruleY([0]),
Plot.dot(filtered_songs, {x: filters.selectX,
y: filters.selectY,
title: "track_name",
fill: "#401487",
stroke: "white",
opacity: 0.9,
r: 6})
]
})
//use SQL to apply filters and return a new array
filtered_songs = db.sql`SELECT
track_name
,album_name
,round(liveness,2) as liveness
,round(valence,2) as valence
,round(energy,2) as energy
,round(acousticness,2) as acousticness
,danceability
,speechiness
,instrumentalness
FROM songs
WHERE
valence >= ${sliders.valence}
and album_name <> 'NA'
and energy >= ${sliders.energy}
and danceability>= ${sliders.danceability}
and acousticness >= ${sliders.acousticness}`
function heatmap(value, minValue, maxValue) {
// Calculate the color scale based on the value's position
var scale = (value - minValue) / (maxValue - minValue);
var color = getColorFromScale(scale);
var fontColor = value > 0.3 ? "white" : "black";
// Create the output div with the scaled background color
return htl.html`
<div style="background-color: ${color}; color: ${fontColor}; text-align: center;">
${value}
</div>
`;
}
function getColorFromScale(scale) {
var colors = d3.schemeBuPu[7]
// Calculate the index of the color in the color ramp based on the scale
var index = Math.floor(scale * (colors.length - 1));
// Get the start and end colors from the color ramp
var startColor = hexToRgb(colors[index]);
var endColor = hexToRgb(colors[index + 1]);
// Helper function to convert hex color to RGB
function hexToRgb(hex) {
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function (m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
] : [255, 255, 255]; // Default to white if invalid hex color
}
// Calculate the intermediate color based on the scale
var color = startColor.map(function (channel, i) {
var range = endColor[i] - channel;
return Math.round(channel + range * (scale * (colors.length - 1) - index));
});
// Return the color in RGB format
return "rgb(" + color.join(",") + ")";
}
Songs
Inputs.table(filtered_songs, {
//how many rows to display
rows:40,
//format table column names
header: {
album_name: "Album Name",
track_name: "Track",
liveness: "Liveness",
energy: "Energy",
acousticness: "Acousticness",
valence: "Valence",
speechiness: "Speechiness"
},
//adjust column width
width: {
track_name: "25%",
album_name: "15%",
liveness: "12%",
valence: "12%",
acousticness: "12%",
energy: "12%",
speechiness: "12%"
},
//align columns
align: {
liveness: "center",
valence: "center",
energy: "center",
acousticness: "center",
speechiness: "center"
},
//format with additional functions
format: {
liveness: x => heatmap(x,0,1),
energy: x => heatmap(x,0,1),
valence: x => heatmap(x,0,1),
acousticness: x => heatmap(x,0,1),
speechiness: x => heatmap(x,0,1)
}
})
// Template credit: This layout is updated from Martien van Steenbergen @martien/horizontal-inputs
template = (inputs) =>
htl.html`<div class="styled">${Object.values(inputs)}</div>
<style>
div.styled {
text-align: left;
column-count: 2
}
div.styled label {
font-weight: bold;
line-height: 200%;
}
div.styled label:not(div>label):after {
content: ":";
}
</style>`