View difference between Paste ID: rFQgEcVi and SzaW21H8
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("")