-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrbo.js
154 lines (145 loc) · 3.71 KB
/
rbo.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
/**
* This calculates the Rank Biased Overlap(RBO) for two sorted lists.
*
* Based on "A Similarity Measure for Indefinite Rankings" William Webber, Alistair Moffat,
* and Justin Zobel (Nov 2010).
*
* For more information, read
* http://www.williamwebber.com/research/papers/wmz10_tois.pdf
*
* Based on the reference by Damian Gryski in Golang available from
* https://github.com/dgryski
*
* @license Licensed under the MIT license.
*
* @author Dag Holmberg
* https://github.com/holmberd
*/
/**
* Object Constructor creates a new RBO state calculation
* @constructor
* @param {number} p - degree (0..1) of top-weightedness of the RBO metric
*/
var RBO = function (p) {
this.p = p;
this.rbo = 0;
this.depth = 0;
this.overlap = 0;
this.shortDepth = -1;
this.seen = new Map();
this.wgt = (1 - p) / p;
this.shortOverlap = -1;
return this;
};
/**
* Calculates the weight of first d rankings with parameter p
* @function calcWeight
* @static
* @param {number} p - degree (0..1) of top-weightedness of the RBO metric
* @param {number} d - ranking
*/
RBO.calcWeight = function(p, d) {
var summa = 0;
for (var i = 1; i < d; i++) {
summa += Math.pow(p, i) / i;
}
return 1 - Math.pow(p, (d - 1)) + ((1 - p) / p) * d * (Math.log( 1 / (1 - p) ) - summa);
};
/**
* Calculates similarity RBO
* @function calculate
* @param {array} s - sorted ranked list
* @param {array} t - sorted ranked list
* @return {function} - extrapolated calculation
*/
RBO.prototype.calculate = function (s, t) {
if (t.length < s.length){
var _t = s;
s = t;
t = _t;
}
for (var i = 0, l = s.length; i < l; i++){
this.update(s[i], t[i]);
}
this.endShort();
if (t.length > s.length){
for (var n = s.length, le = t.length; n < le; n++){
this.updateUneven(t[n]);
}
}
return this.calcExtrapolated();
};
/**
* Calculates the estimate beyond the original observation range
* @function calcExtrapolated
* @return {number} - similarity RBO scores achieved
*/
RBO.prototype.calcExtrapolated = function () {
var pl = Math.pow(this.p, this.depth);
if (this.shortDepth == -1) {
this.endShort();
}
return this.rbo + ((this.overlap-this.shortOverlap)/(this.depth)+((this.shortOverlap)/(this.shortDepth)))*pl;
};
/**
* Adds elements from the two lists to our state calculation
* @function RBO.prototype.update
* @param {element} e1
* @param {element} e2
*/
RBO.prototype.update = function (e1, e2) {
if (this.shortDepth != -1){
console.log("RBO: update() called after EndShort()");
return false;
}
if (e1 == e2){
this.overlap++;
}
else {
if (this.seen.has(e1)){
this.seen.set(e1, false);
this.overlap++;
}
else {
this.seen.set(e1, true);
}
if(this.seen.has(e2)){
this.seen.set(e2, false);
this.overlap++;
}
else {
this.seen.set(e2, true);
}
}
this.depth++;
this.wgt *= this.p;
this.rbo += (this.overlap / this.depth) * this.wgt;
};
/**
* Indicates the end of the shorter of the two lists has been reached
* @function endShort
*/
RBO.prototype.endShort = function () {
this.shortDepth = this.depth;
this.shortOverlap = this.overlap;
};
/**
* Adds elements from the longer list to the state calculation
* @function UpdateUneven
* @param {element}
*/
RBO.prototype.updateUneven = function (e) {
if (this.shortDepth == -1) {
console.log("RBO: UpdateUneven() called without EndShort()");
return false;
}
if (this.seen[e]) {
this.overlap++;
this.seen[e] = false;
}
this.depth++;
this.wgt *= this.p;
this.rbo += (this.overlap / this.depth) * this.wgt;
this.rbo += ((this.shortOverlap*(this.depth-this.shortDepth)) / (this.depth*this.shortDepth)) * this.wgt;
};
module.exports = RBO;