Files
tunmnlu/task_2/others-answer/omsa-main/CS-6242-OAN/hw2/Q4/interactive.js
louiscklaw 9035c1312b update,
2025-02-01 02:09:32 +08:00

433 lines
14 KiB
JavaScript

//------------------------1. PREPARATION------------------------//
//-----------------------------SVG------------------------------//
const margin = 250
const global_height = 500
const global_width = 960
const width = global_width - margin
const height = global_height - margin
// we are appending SVG first
const svg_linechart = d3.select("body")
.append("svg")
.attr("width",global_width)
.attr("height", global_height)
//-----------------------------DATA-----------------------------//
const timeConv = d3.timeParse("%Y-%m-%d");
const dataset = d3.dsv(",","average-rating.csv");
dataset.then(function(data) {
//Create rawArr for creating the line data in chart a
var rawArr = data.map(function(d,i) {
return d.year+'|'+Math.floor(d.average_rating)
});
function countAndSort(arr) {
return Object.entries(arr.reduce((prev, curr) => (prev[curr] = ++prev[curr] || 1, prev), {})).sort((a,b) => b[1]-a[1])
} ;
var frequencyCount = countAndSort(rawArr).map(function(d,i){
return {
year: d[0].split('|')[0],
average_rating: parseInt(d[0].split('|')[1]),
frequency: parseInt(d[1]),
}
})
var ratingPerYear ={};
frequencyCount.forEach(element => {
var makeKey = element.year;
if(!ratingPerYear[makeKey]) {
ratingPerYear[makeKey] = [];
}
ratingPerYear[makeKey].push(element.average_rating);
});
function range(start, end) {
return Array(end - start + 1).fill().map((_, idx) => start + idx)
}
var fullRatingsRange = range(0,9)
for (let i = 0; i < Object.keys(ratingPerYear).length; i++) {
year = Object.keys(ratingPerYear)[i]
if (year >= 2015 && year <= 2019) {
currentRatings = ratingPerYear[year]
missingRatings = fullRatingsRange.filter(x => !currentRatings.includes(x));
missingRatings.map(function(d){
missingDict = {
year: year,
average_rating: d,
frequency: 0
}
frequencyCount.push(missingDict)
})
}
}
console.log(ratingPerYear)
function fieldSorter(fields) {
return function (a, b) {
return fields
.map(function (o) {
var dir = 1;
if (o[0] === '-') {
dir = -1;
o=o.substring(1);
}
if (a[o] > b[o]) return dir;
if (a[o] < b[o]) return -(dir);
return 0;
})
.reduce(function firstNonZeroValue (p,n) {
return p ? p : n;
}, 0);
};
}
var frequencyCountsSorted = frequencyCount.sort(fieldSorter(['year','average_rating']))
var frequencyGroupedYear ={};
frequencyCountsSorted.forEach(element => {
var makeKey = element.year;
if(!frequencyGroupedYear[makeKey]) {
frequencyGroupedYear[makeKey] = [];
}
frequencyGroupedYear[makeKey].push({
id : makeKey,
average_rating: element.average_rating,
frequency: element.frequency
});
});
var datasetLineChart = []
for (let i = 0; i < Object.keys(frequencyGroupedYear).length; i++) {
year = Object.keys(frequencyGroupedYear)[i]
if (year >= 2015 && year <= 2019) {
var nestedDict = {
id: year,
values: frequencyGroupedYear[year]
}
datasetLineChart.push(nestedDict)
}
}
//Create rawArr for creating the line data in chart a
var rawArrMovies = data.map(function(d,i) {
if (d.year >= 2015 && d.year <=2020) {
return {year:d.year,
average_rating: parseInt(Math.floor(d.average_rating)),
users_rated: parseInt(d.users_rated),
name: d.name
}
}});
var rawArrMoviesFiltered = rawArrMovies.filter(function(x) {
return x !== undefined;
});
var moviesSorted = rawArrMoviesFiltered.sort(fieldSorter(['year','average_rating','-users_rated']))
console.log(moviesSorted)
function top5Movies(selectedPoint) {
var moviesYearRating = []
moviesSorted.forEach(function(d, i){
if (d.year == selectedPoint.id && d.average_rating == selectedPoint.average_rating){
movieDict = {
year:d.year,
name:d.name.substring(0,10),
users_rated:d.users_rated,
average_rating:d.average_rating
}
moviesYearRating.push(movieDict)
}
})
//Now sort the result by user_rated
var top5MoviesPreSort = moviesYearRating.sort(fieldSorter(['-users_rated'])).slice(0,5)
var top5MoviesFinal = top5MoviesPreSort.sort(fieldSorter(['users_rated']))
return top5MoviesPreSort
}
//----------------------------SCALES----------------------------//
const xScale = d3.scaleTime().range([0,width]);
const yScale = d3.scaleLinear().rangeRound([height, 0]);
const colors = d3.scaleOrdinal(d3.schemeCategory10);
xScale.domain([0, d3.max(datasetLineChart, function(c) {
return d3.max(c.values, function(d) {
return parseInt(d.average_rating); });
})
]);
yScale.domain([(0), d3.max(datasetLineChart, function(c) {
return d3.max(c.values, function(d) {
return d.frequency; });
})
]);
//-----------------------------AXES-----------------------------//
const yaxis = d3.axisLeft()
.scale(yScale);
const xaxis = d3.axisBottom()
.tickFormat(d3.format("d"))
.scale(xScale);
//----------------------------LINES-----------------------------//
const line = d3.line()
.x(function(d) { return xScale(d.average_rating)+100; })
.y(function(d) { return yScale(d.frequency)+50; });
let id = 0;
const ids = function () {
return "line-"+id++;
}
const point_ids = function () {
return "point-"+id++;
}
//-------------------------2. DRAWING---------------------------//
//-----------------------------AXES-----------------------------//
svg_linechart.append("g")
.attr("id","line_chart_title")
svg_linechart.select("#line_chart_title")
.append("text")
.attr("transform", "translate(300,0)")
.attr("y", 25)
.attr("font-size", "20px")
.text("Board games by Rating 2015-2019");
svg_linechart.append("g")
.attr("id","credit")
svg_linechart.select("#credit")
.append("text")
.attr("y", height + 100 )
.attr("x", width+100)
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("mpearl3");
svg_linechart.append("g")
.attr("transform", "translate(100," + (height+50) + ")")
.attr("id", "x-axis-lines")
.call(xaxis);
svg_linechart.append("text")
.attr("id", "x-axis label")
.attr("transform", "translate(400,0)")
.attr("y", height + 100)
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("Rating");
svg_linechart.append("g")
.attr("id", "y-axis-lines")
.attr("transform", "translate(100,50)")
.call(yaxis);
svg_linechart.append("text")
.attr("id", "y-axis label")
.attr("transform", "rotate(-90)")
.attr("y", height - 150)
.attr("x", -150)
.attr("dy", "-5.1em")
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("Count");
//----------------------------LINES-----------------------------//
const frequencyLines = svg_linechart.append("g").attr("id","lines")
.selectAll("lines")
.data(datasetLineChart)
.enter();
frequencyLines.append("path")
.attr("fill", "none")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d, i) {
return colors(d.id);
});
frequencyPoints = svg_linechart.append("g")
.attr("id","circles")
frequencyPoints
.selectAll(".dot")
.data(datasetLineChart)
.enter()
.append("g")
.attr("id",point_ids)
.selectAll("circle")
.data(function(d){return d.values})
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.average_rating)+100;
})
.attr("cy", function(d,i) { return yScale(d.frequency)+50; })
.attr("r", 4)
.style("fill", function(d) {
return colors(d.id);
}
)
.on("mouseover", function(d) {
d3.select(event.currentTarget).attr("r", "12");
const svg_barchart = d3.select("body")
.append("svg")
.attr("id","barchart")
.attr("width",global_width)
.attr("height", global_height)
const topMoviesSelection = top5Movies(d)
if (d.frequency > 0) {
//----------------------------SCALES----------------------------//
const xScaleBar = d3.scaleLinear().range([0,width]);
const yScaleBar = d3.scaleBand()
.range([height, 0]);
xScaleBar.domain([0, d3.max(topMoviesSelection,function(c) {
return c.users_rated;
})]);
var movieNames = []
topMoviesSelection.forEach(
function(d){
movieNames.push(d.name);
}
);
yScaleBar.domain(movieNames);
//-----------------------------AXES-----------------------------//
const yaxisBar = d3.axisLeft()
.scale(yScaleBar);
const xaxisBar = d3.axisBottom()
.tickFormat(d3.format("d"))
.scale(xScaleBar);
//-----------------------------DRAWING SUB-PLOT-----------------------------//
svg_barchart.append("g")
.attr("id","bar_chart_title")
.data(topMoviesSelection)
.enter()
// const countLinesA = svg_a.select("#plot-a").append("g").attr("id","lines-a")
// .selectAll("lines")
// .data(countSlices)
// .enter();
svg_barchart
.select("#bar_chart_title")
.append("text")
.datum(function(d) {
return {
year: d.year,
value: d.average_rating
}})
.attr("transform", "translate(300,0)")
.attr("y", 25)
.attr("font-size", "20px")
.text(function(d) {
return 'Top 5 most rated games of '+d.year+' with rating '+d.value
});
svg_barchart.append("g")
.attr("transform", "translate(100," + (height+50) + ")")
.attr("id", "x-axis-bars")
.call(xaxisBar);
svg_barchart.append("g")
.attr("id", "bar_x_axis_label");
svg_barchart.select("#bar_x_axis_label")
.append("text")
.attr("transform", "translate(400,0)")
.attr("y", height +100)
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("Number of Users");
svg_barchart.append("g")
.attr("id","y-axis-bars")
.attr("transform", "translate(100,50)")
.call(yaxisBar);
svg_barchart.append("g")
.attr("id", "bar_y_axis_label");
svg_barchart.select("#bar_y_axis_label")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", height - 150)
.attr("x", -150)
.attr("dy", "-5.1em")
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("Games");
const bars = svg_barchart.append("g")
.attr("id","bars")
.selectAll("myRect")
.data(topMoviesSelection)
.enter();
bars.append("rect")
.attr("x", xScaleBar(0)+100 )
.attr("y", function(d) { return yScaleBar(d.name)+50; })
.attr("width", function(d) { return xScaleBar(d.users_rated); })
.attr("height", yScaleBar.bandwidth() )
.attr("fill", "#69b3a2");
}})
.on("mouseout", function(d){
d3.select(event.currentTarget).attr("r", 4);
d3.select("#barchart").remove()
// svg_barchart.selectAll(".text").remove()
// svg_barchart.select("#x-axis-bars").remove()
// svg_barchart.select("#y-axis-bars").remove()
// svg_barchart.select("#bar-chart-title").remove()
// svg_barchart.select("#bar_x_axis_label").remove()
// svg_barchart.select("#bar_y_axis_label").remove()
// svg_barchart.selectAll('#bars').remove()
// svg_barchart.select("#bar_chart_title").remove()
});
var legend_years = ["2015","2016","2017","2018","2019"]
legend = svg_linechart.append("g")
.attr("id","legend")
// Handmade legend
legend_years.forEach(function(d,i) {
legend.append("circle").attr("cx",(width+40)+(1)*8).attr("cy",95+(i+1)*15).attr("r", 6).style("fill", colors(d))
legend.append("text").attr("x", (width+50)+(1)*8).attr("y", 95+(i+1)*15).text(d).style("font-size", "15px")
}
)
});