SHOW:
|
|
- or go back to the newest paste.
1 | from django.core.management.base import BaseCommand, CommandError | |
2 | from math import sqrt, fabs, pow | |
3 | from copy import deepcopy | |
4 | from collections import defaultdict | |
5 | ||
6 | from ratings.models import Rating, Participant, ParticipantRelation | |
7 | ||
8 | class RelationEqualizer(object): | |
9 | def __init__(self, rating): | |
10 | self._rating = rating | |
11 | self._prel = {} | |
12 | self._pmap = {} | |
13 | self._antifraud = {} | |
14 | self._uvn_iterations = 10 # user vector normalization iterations | |
15 | self._tn_iterations = 5 # table normalization iterations | |
16 | ||
17 | def _print(self): | |
18 | skeys = sorted(self._pmap.keys(), key=lambda x: -self._pmap[x].score) | |
19 | for row_id in skeys: | |
20 | vals = [ u'%20s' % self._pmap[row_id].user.username ] | |
21 | total = 0.0 | |
22 | count = 0.0 | |
23 | for col_id in skeys: | |
24 | rel = self._prel[row_id][col_id] | |
25 | if rel is None: | |
26 | vals.append('U') | |
27 | elif rel > 0: | |
28 | vals.append('B') | |
29 | elif rel < 0: | |
30 | vals.append('.') | |
31 | else: | |
32 | vals.append('=') | |
33 | vals.append('%5.1f' % self._user_scores(row_id)) | |
34 | print u' '.join(vals) | |
35 | ||
36 | def _user_scores(self, user): | |
37 | count = len(self._pmap) | |
38 | if count == 0: | |
39 | return 0.0 | |
40 | score = 0.0 | |
41 | ||
42 | for rel in self._prel[user].itervalues(): | |
43 | if rel is None: | |
44 | continue | |
45 | if rel >= 0: | |
46 | score += 1 | |
47 | else: | |
48 | score -= 1 | |
49 | return pow((score / count + 1.0) * 50, 0.5) * 10 | |
50 | ||
51 | def _load_data(self): | |
52 | pmap = {} | |
53 | prel = {} | |
54 | ||
55 | rlist = ParticipantRelation.objects.filter(subject__rating=self._rating).all() | |
56 | for r in rlist: | |
57 | if not r.object.active or not r.subject.active: | |
58 | continue | |
59 | if not r.subject.pk in pmap: | |
60 | pmap[r.subject.pk] = r.subject | |
61 | if not r.object.pk in pmap: | |
62 | pmap[r.object.pk] = r.object | |
63 | if r.subject.pk not in prel: | |
64 | prel[r.subject.pk] = {} | |
65 | if r.object.pk not in prel: | |
66 | prel[r.object.pk] = {} | |
67 | prel[r.subject.pk][r.object.pk] = r.value | |
68 | ||
69 | for x in pmap.iterkeys(): | |
70 | for y in pmap.iterkeys(): | |
71 | if y not in prel[x]: | |
72 | prel[x][y] = None | |
73 | if x == y: | |
74 | prel[x][y] = 0.0 | |
75 | ||
76 | self._pmap = pmap | |
77 | self._prel = prel | |
78 | ||
79 | def _load_antifraud(self): | |
80 | antifraud = {} | |
81 | prel = self._prel | |
82 | competitors_number = len(prel) | |
83 | for xid, rels in prel.iteritems(): | |
84 | antifraud[xid] = 1.0 | |
85 | count = 0.0 | |
86 | frauds = 0.0 | |
87 | for yid, rel in rels.iteritems(): | |
88 | if prel[xid][yid] is None or prel[yid][xid] is None: | |
89 | continue | |
90 | ||
91 | count += 1 | |
92 | if prel[xid][yid] * prel[yid][xid] < 0 or prel[xid][yid] == 0 and prel[yid][xid] == 0: | |
93 | continue | |
94 | ||
95 | if prel[xid][yid] * prel[yid][xid] > 0: | |
96 | frauds += 1.0 | |
97 | elif prel[xid][yid] * prel[yid][xid] == 0: | |
98 | frauds += 2.0 /3.0 | |
99 | if count > 0: | |
100 | antifraud[xid] = pow(1.0 - frauds / count, 5) | |
101 | self._antifraud = antifraud | |
102 | ||
103 | def _stronger_weaker_equals(self, id, weight, s, e, w): | |
104 | ||
105 | for yidx in self._prel.iterkeys(): | |
106 | rel = self._prel[id][yidx] | |
107 | if rel is not None: | |
108 | if rel > 0: | |
109 | w[yidx] += weight * self._antifraud[id] | |
110 | elif rel < 0: | |
111 | s[yidx] += weight * self._antifraud[id] | |
112 | elif rel == 0: | |
113 | e[yidx] += weight * self._antifraud[id] | |
114 | ||
115 | rel = self._prel[yidx][id] | |
116 | if rel is not None: | |
117 | if rel < 0: | |
118 | w[yidx] += weight * self._antifraud[yidx] | |
119 | elif rel > 0: | |
120 | s[yidx] += weight * self._antifraud[yidx] | |
121 | elif rel == 0: | |
122 | e[yidx] += weight * self._antifraud[yidx] | |
123 | ||
124 | def _update_user_vector(self, user): | |
125 | ||
126 | stronger = defaultdict(lambda: 0.0) # players that stronger than user | |
127 | equals = defaultdict(lambda: 0.0) | |
128 | weaker = defaultdict(lambda: 0.0) | |
129 | equals[user] = 1 | |
130 | ||
131 | for i in xrange(0, self._uvn_iterations): | |
132 | dummy = defaultdict(lambda: 0.0) | |
133 | s = defaultdict(lambda: 0.0) | |
134 | w = defaultdict(lambda: 0.0) | |
135 | e = defaultdict(lambda: 0.0) | |
136 | ||
137 | for yidx, yval in stronger.iteritems(): | |
138 | self._stronger_weaker_equals(yidx, yval, s, s, dummy) | |
139 | ||
140 | for yidx, yval in equals.iteritems(): | |
141 | self._stronger_weaker_equals(yidx, yval, s, e, w) | |
142 | ||
143 | for yidx, yval in weaker.iteritems(): | |
144 | self._stronger_weaker_equals(yidx, yval, dummy, w, w) | |
145 | ||
146 | for k in set(s.keys()) | set(e.keys()) | set(w.keys()): | |
147 | sum = s[k] + w[k] + e[k] | |
148 | if sum > 0: | |
149 | s[k] /= sum | |
150 | e[k] /= sum | |
151 | w[k] /= sum | |
152 | ||
153 | stronger = s | |
154 | equals = e | |
155 | weaker = w | |
156 | ||
157 | result = {} | |
158 | for idx in self._prel.iterkeys(): | |
159 | result[idx] = w[idx] - s[idx] | |
160 | if fabs(result[idx]) <= 1.0 / 3: | |
161 | result[idx] = 0.0 | |
162 | return result | |
163 | ||
164 | def relations(self): | |
165 | self._load_data() | |
166 | for i in xrange(0, self._tn_iterations): | |
167 | new_prel = {} | |
168 | self._load_antifraud() | |
169 | for user in self._prel.iterkeys(): | |
170 | new_prel[user] = self._update_user_vector(user) | |
171 | self._prel = new_prel | |
172 | return self._prel | |
173 | ||
174 | def update_rates(self): | |
175 | self._load_data() | |
176 | self._print() | |
177 | prel = self.relations() | |
178 | print "" | |
179 | self._print() | |
180 | for user in prel.iterkeys(): | |
181 | uname = self._pmap[user].user.username.encode('utf8', 'ignore') | |
182 | self._pmap[user].score = self._user_scores(user) | |
183 | self._pmap[user].save() | |
184 | print "User %s scores %f" % (uname, self._pmap[user].score) | |
185 | ||
186 | class Command(BaseCommand): | |
187 | help = 'ratings normalization' | |
188 | ||
189 | def handle(self, *args, **kwargs): | |
190 | for r in Rating.objects.all(): | |
191 | self.stdout.write("Processing rating %s" % r.title) | |
192 | req = RelationEqualizer(r) | |
193 | req.update_rates() | |
194 | self.stdout.write("") |