-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathutils.js
239 lines (214 loc) · 8.24 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
function get_distance(x1,x2,y1,y2){
return Math.sqrt(Math.pow(x1-x2,2)+Math.pow(y1-y2,2))
}
//take the artists for a song and parse them into an array of artists
function parseArtists(s){
//console.log("parsing artists for:",s)
let artistsList = s.replaceAll(/\[|\]|\'/g, '').split(',');
//remove first character from every element except the first one
for (let i=1;i<artistsList.length;i++){
artistsList[i]=artistsList[i].trim();
}
//console.log("artistsList:",artistsList)
return artistsList
}
function get_k_nearest_elements(this_artist,selected_elem){
console.log("selecting similar to:",selected_elem)
let nearest_scatter=null;
if (this_artist) { nearest_scatter=scatter_artists }
else { nearest_scatter=scatter_songs }
let nearest_elements=[]
nearest_scatter.selectAll("circle")
.each(function(d){
if (nearest_elements.length>=2){
nearest_elements.sort(function(a, b) {return a["distance"] - b["distance"];});
}
let distance=get_distance(d[0],selected_elem[0],d[1],selected_elem[1])
//ignore selected element
if (this_artist && d[2]["artists"]==selected_elem[2]["artists"]){ return }
if (!this_artist && d[2]["id"]==selected_elem[2]["id"]){ return }
//select K nearest elements of selected artist
if (nearest_elements.length<K_nearest){
nearest_elements.push({"data":d,"distance":distance})
return
}
//compare with nearest elements
for (let i=0;i<K_nearest;i++){
if (distance<nearest_elements[i]["distance"]){
//add this element to the list
nearest_elements.splice(i,0,{"data":d,"distance":distance})
//delete last element of array
if (nearest_elements.length>K_nearest){
nearest_elements.pop()
}
break
}
}
})
console.log("nearest elements:",nearest_elements)
return nearest_elements
}
function getMaxMin(data, key) {
let max = d3.max(data, d => d[key]);
let min = d3.min(data, d => d[key]);
return [min, max];
}
function getMuStd(data, key) {
let std = d3.deviation(data, d => d[key]);
let med = d3.median(data, d => d[key]);
console.log("mu:",med,"std:",std)
return [med, std];
}
function norm_min_max(value, min, max) {
return (value - min) / (max - min);
}
function normalize(value,mean,std){
return (value-mean)/std
}
function getPCA(pca_data){
//data must be in matrix form
var PCAvectors = PCA.getEigenVectors(pca_data);
console.log("PCAvectors:",PCAvectors)
var new_data_=PCA.computeAdjustedData(pca_data,PCAvectors[0],PCAvectors[1])
var new_data_=new_data_["adjustedData"]
//data is now in form [[1,3,5],[2,4,6]]
//we need data in this form
//data=[[1,2],[3,4],[5,6]...]
var new_data=[]
for(var i=0;i<new_data_[0].length;i++){
new_data.push([new_data_[0][i],new_data_[1][i]])
}
console.log("data after PCA:",new_data)
return new_data
}
function getHistValues(data, bins, category, min, max) {
let vals = [];
// Special case for loudness given that min=-60dB
if (category == "loudness") {
for (let i=0; i<bins; i++) {
vals.push(d3.count(data, function(d) {
return (d[category] <= (min-max)*i/bins && d[category] > (min-max)*(i+1)/bins) ? 1 : undefined;
}))
}
}
else{
for (let i=0; i<bins; i++) {
vals.push(d3.count(data, function(d) {
return (d[category] >= (max-min)*i/bins && d[category] < (max-min)*(i+1)/bins) ? 1 : undefined;
}))
}
}
//console.log(vals);
return vals;
}
/**
* Rounds a number to the closest bin value that is <= number
*/
function round2Bin(n, cat, bins, min, max) {
let i = 0
// Special case for loudness given that min=-60dB
if (cat == "loudness") {
while ((min-max)*i/bins >= n) {i++};
return (min-max)*(i-1)/bins; // i-1 so that the selection starts from the bar that was clicked, not from the following one
}
else {
while ((max-min)*i/bins <= n) {i++};
return (max-min)*(i-1)/bins;
}
}
function compute_boxplot_data(songs){
//compute boxplot data for a given category
var boxplot_data = []
for(var i=0;i<categories.length;i++){
//dtaa for this category
category_data={}
category_data.category=categories[i]
//if null songs then all 0 values
if (songs.length>0) {
category_data.min = d3.min(songs, function(d) { return d[categories[i]]; });
category_data.min=norm_min_max(category_data.min, cat_limits[i][0], cat_limits[i][1])
if (!category_data.min) category_data.min=0
category_data.max = d3.max(songs, function(d) { return d[categories[i]]; });
category_data.max=norm_min_max(category_data.max, cat_limits[i][0], cat_limits[i][1])
if (!category_data.max) category_data.max=0
category_data.median = d3.median(songs, function(d) { return d[categories[i]]; });
category_data.median=norm_min_max(category_data.median, cat_limits[i][0], cat_limits[i][1])
if (!category_data.median) category_data.median=0
category_data.q1 = d3.quantile(songs, 0.25, function(d) { return d[categories[i]]; });
category_data.q1=norm_min_max(category_data.q1, cat_limits[i][0], cat_limits[i][1])
if (!category_data.q1) category_data.q1=0
category_data.q3 = d3.quantile(songs, 0.75, function(d) { return d[categories[i]]; });
category_data.q3=norm_min_max(category_data.q3, cat_limits[i][0], cat_limits[i][1])
if (!category_data.q3) category_data.q3=0
//interqauntile range (the box)
category_data.iqr = category_data.q3 - category_data.q1;
//baffi
category_data.upper = category_data.q3 + 1.5 * category_data.iqr;
category_data.lower = category_data.q1 - 1.5 * category_data.iqr;
}
else {
category_data.min=0
category_data.max=0
category_data.median=0
category_data.q1=0
category_data.q3=0
category_data.iqr = 0;
//baffi
category_data.upper = 0;
category_data.lower = 0;
}
boxplot_data.push(category_data);
}
return boxplot_data
}
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
if (this.parentNode) {
this.parentNode.appendChild(this);
}
});
};
function groupByArtist(data){
let groupedSongs=[]
let consideredArtists=[]
for(var i=0;i<data.length;i++){
let song=data[i]
let artist=song["artists"]
//if artist not in considered artists then add it to considered artists
if(consideredArtists.indexOf(artist)==-1){
consideredArtists.push(artist)
groupedSongs.push([artist,[]])
}
//find the index of the artist in the grouped songs array
let index=consideredArtists.indexOf(artist)
groupedSongs[index][1].push(song)
//same for every co-artist
let coartists=song["co_artists"]
for(var j=0;j<coartists.length;j++){
let coartist=coartists[j]
if(consideredArtists.indexOf(coartist)==-1){
consideredArtists.push(coartist)
groupedSongs.push([coartist,[]])
}
let index=consideredArtists.indexOf(coartist)
groupedSongs[index][1].push(song)
}
}
return groupedSongs
}
function getRadiusBySongsNumber(name,this_artist,default_radius){
//console.log("creating scatter:",d);
if(!this_artist) return default_radius
let max_value=default_radius*3;
for (var i = 0; i < songByArtists_.length; i++) {
let temp=songByArtists_[i]
if (temp[0]==name) {
//map value between 3 and 10
let numSongs=temp[1].length;
if(!temp[1].length) numSongs=1
if(numSongs>maxSongsByArtist) return max_value;
return default_radius+max_value*((numSongs)/maxSongsByArtist)
}
}
return default_radius;
}