From c31de42a99f42e64efeb97ba404a965781488465 Mon Sep 17 00:00:00 2001 From: art Date: Sat, 5 Oct 2019 16:18:49 -0500 Subject: [PATCH] analysis.py v 1.1.4.000 --- .../__pycache__/analysis.cpython-36.pyc | Bin 20550 -> 20959 bytes .../__pycache__/analysis.cpython-37.pyc | Bin 21322 -> 20572 bytes .../__pycache__/regression.cpython-37.pyc | Bin 6981 -> 6984 bytes .../__pycache__/trueskill.cpython-37.pyc | Bin 0 -> 32171 bytes data analysis/analysis/analysis.py | 26 +- .../{trueskill/__init__.py => trueskill.py} | 777 +++++++++++------- data analysis/analysis/trueskill/__about__.py | 11 - .../__pycache__/__about__.cpython-37.pyc | Bin 414 -> 0 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 25214 -> 0 bytes .../__pycache__/backends.cpython-37.pyc | Bin 4073 -> 0 bytes .../__pycache__/deprecated.cpython-37.pyc | Bin 4108 -> 0 bytes .../__pycache__/factorgraph.cpython-37.pyc | Bin 7279 -> 0 bytes .../__pycache__/mathematics.cpython-37.pyc | Bin 9250 -> 0 bytes data analysis/analysis/trueskill/backends.py | 135 --- .../analysis/trueskill/deprecated.py | 134 --- .../analysis/trueskill/factorgraph.py | 199 ----- .../analysis/trueskill/mathematics.py | 260 ------ 17 files changed, 504 insertions(+), 1038 deletions(-) create mode 100644 data analysis/analysis/__pycache__/trueskill.cpython-37.pyc rename data analysis/analysis/{trueskill/__init__.py => trueskill.py} (51%) delete mode 100644 data analysis/analysis/trueskill/__about__.py delete mode 100644 data analysis/analysis/trueskill/__pycache__/__about__.cpython-37.pyc delete mode 100644 data analysis/analysis/trueskill/__pycache__/__init__.cpython-37.pyc delete mode 100644 data analysis/analysis/trueskill/__pycache__/backends.cpython-37.pyc delete mode 100644 data analysis/analysis/trueskill/__pycache__/deprecated.cpython-37.pyc delete mode 100644 data analysis/analysis/trueskill/__pycache__/factorgraph.cpython-37.pyc delete mode 100644 data analysis/analysis/trueskill/__pycache__/mathematics.cpython-37.pyc delete mode 100644 data analysis/analysis/trueskill/backends.py delete mode 100644 data analysis/analysis/trueskill/deprecated.py delete mode 100644 data analysis/analysis/trueskill/factorgraph.py delete mode 100644 data analysis/analysis/trueskill/mathematics.py diff --git a/data analysis/analysis/__pycache__/analysis.cpython-36.pyc b/data analysis/analysis/__pycache__/analysis.cpython-36.pyc index cd6c08c5376f69be15efdce169f91a0567db2894..ff3808987c2157f8db9d89827eb2adc61a9d6fbe 100644 GIT binary patch delta 6975 zcmcIoYj7J^72bzlmSszp<+tNk?8b^6zn!-oC$ZzSaUQkPq}?`A*4~Y(WVKT6N{Ox7 z5sgzQg@&fLrEOXuO=(Mkp)g@!3d7Jpl)}IaKbirSp`|cP$qd6l`-2(Y=iF5!S#{hD z1JdZ|-gD3Ee)rsS@9rCy`FCF6d-_XC+%5KJj#M7uxZiPkA0MN~|tt471m9mj)XnQ4ZM#?Ao#yO=%sXfmrwQAX- zDEXB-#de;P%2kI_udI5MR~i)id2W%H0$EO@;s8#XmnyQXCdJ8wDzlts#l<*PSx$>m z#5mPAII8bN&57DYPFbzA9v781#eJR!N5yRXHgLU0DP|gVH#C%X#ltxDsw16=FQ}|# zEKgRbLn&dLRas7_Qpz|DS|Qn?dt97@9OF4ldn;)P}$SB+1&)6 zbxlOo<*2H_f0(F-5sB$dGn#A|O^I03WR}-S57+{o$jg%pYmt+&zL2ViBf6T?8fWaQ z^}@Sl)$W|QDXLn`bXZE%8>s5fnKvDl(sj(=eL>z7Pec+yo)keBdPND6RE?7nO{1fM zq@xzNq?Ud^u)DGnns30*fE?#sJi-19N1xvtpuH870ZS5iqz;J)VTo&(rZ;m zM=aYYUV-98dW=M6Es|1$B54DTX_HCVFh!t+Mw~Ewof_3o*Oy@nClYj$tU*)MRGoCt zH8s73VY@)|eX7;GMxUwm(ZO1Umel#^E42z=LQCrm`u94o?=fEgPJ%nZ&%+YrDdLJ#WzmNCP`|D5AZL7SrzHTkOT;Jd1e@Lw16nhO97EpfP2`N^-k2HWMNz@`b zonF;7fpL<4AP`$D42wS-mqa5HmK-3V%H;aL;X{T{YDE(C5-)vgRY(0Gimn18ITEUF z#E2;*Vx$AP4YaP|KvfePi|FxW!jPP?WC9zcm!50rE(8Vh>R<1n>faFdfu&#TkySXtv0UfD07LUh_h#n3R#q`9c4V9dh z6R=AS(~(inhG8XnF{q7VteAM5SSqv#Gq^kOgDm{*grAWB66O|!+39(1lsh6Vh|sgm z?wS{&H@uLx&)XI5q)0|tYSzI z+=n@kvCVVy{GtHKBJLyE^a$EXIRqj(CSh87SSm`Wax|1sqv<%=fde)IPz4>58wZRK z04I|}IN)KDFbZmhWWye6N8_L^N6BH3DOeBYX)j`fb%GOZf}6MTZr;TQc#q)XOZifP z$sk}f-8@hcKu0dp7qYf?CPUtAt}o0e4+w_nLQ56(wsh=7HPateWj$n^O3Fl4La@#; zlH0LCf!qy3F&jEt8ulcf+e44-z?|vwYo`a?B(Zd2 zRA7>xZ8xTJ`l2b~QG&8$8VD2=m!6D1goE`kY-S zE10GSD$sO9x9Wyui|MLi+6*{&$Of=tiZgP8$h3ZKb75A?F#I~bb8Ud1q{r888)$-l zkeA%LdM2c(rz0~TI_(hkH?wOZq$(!qFV{+-{-@3wy0;@7MicB@2s*6bV1MfhLAL2U zqejB_Cm^`#h-?t7QhI7WCS>y$ZUd8c7WjoCUMZ{1dFYWDZHrCTp=9$_NbngifuS2E;<$_4JAU zDtdk0_G2+rXhM>i3r0@jSuVzI*|$Jrvsex@=Kt3PaNUN^RsmdxrkQ!CWh#kA5hv3L z2RH5@1{gVw{5-VC8Dz{N$<2vP2j}_j5{%l=)tb9k#)vJ8pzEeHvvWxbjjs&d42b-K zzTK^5pbPgr(p&(W%Zfh_HtO$fX}cB3{zy*^lnb5Ksyx!0-pYU~A`2K;7ATU&?pY)( zVzx>-xNO$4oS|cV&27s-^?q($sxhHDEO#&Wm4$8!7z==TWWJfl2QYy7Y!oZwh6V5U zw5tC&;C8nE(mfxE8QLxIdMgP26A;8Ya<;#cJ~uFz{wM(1Otzr24+DtYk0E&w$udZa zZ~|s3E<>_0_pH%15eq|)Ug&qwOM}IGZWY;;QN;7)uk_D@3e51X^_SBBFBD60Vt5_p zpw3y}RNix1*Vt?v4>YQ8ID2adZ$$+E3xoqJAZ!IeqZ^#`*+KWj{{~?^om~R`%U}on zVB-M5+`cIt`4G$zoDQCiAKL23!xO+dRx(?WohqmUPh)W2CR7qN3iAmUzJdBSWAe6b zzGA^zY}wy{26rHlkuXbkBO%Y6EGVIQ5IZte02CzKOiVL~j!h>Xs&MSww2-zU;vSCf z+Tw#q4sU50zbUY6o(zhWvsMAeHNw@`x2)$|cSHMNu$0Zqw8!F!NK_K1#z(CKY&(pS zCR#hx?qG-44!V1&wFNJe@JLT$NjSpCQDh^MO-ME)IY%!HwXVkbnYK7wgG~XZTd-g8 zvbBz08(Pm#UiA!D@M+5zsxKm8h2T849ztTd$JWP@d;-bCK!PRYlh{6o1Op?FAjw6= z0{Rs4K8@s2Bp4aFfaEhsSY%#C+4-dca~FMe+dxlV{m8o(!e#s@>V<#=Ye?RD(DISN zG>g0e1sahwAvuPGB`C|7VeF0|!Sl@te$L$I2g&b?D(6EVYJS5ty<<-wQ> z*b4*ac*qmn*7S!X@4@5ytJ`;vdPCL=mQcugUV6JBGFv=R(1!g5|=nV;IVbJOiFg2uF7g zZF)K{gagZjV8;-Y=rY>B+Ie{YQ>bwX$(?8?QwP{om@(uQOv<`=pgnenhlfTtpYmoR zdU*8E{(Dj9J|LzDcXfgRTGp4Jo+kub!Gd{mbT%8OkSe!qUL6=4;y2);0@9OcYzhfm z<=j#q1m4*j$__5oo(bsHUH7GLMK_((WtU3wd2~_+Bv@h!q0^=_1QqE}Ok-0amvJ;Z zQSt@svWc=iItX1*3d3)VJc)0s;5CLpFOCn?uq{>)`&}R~Pv0JIbOth0F5XS}G&*Nt zgrNbS-)yrY8-O-@OB8gO4tVg;;1vK1WeX0lP;l8yTPPtX38pB){D)%3vg<`;yoBUs zBws>;QNinOG-dL#B!Z$u|8aYH;Sw=8(B@|Ieb%yjo~5ATSbm)+q=g z>sqqo8Y?eQEbzn0`$nnvMetja) zUa;1P$~V!p4H}#f$Qk`$V((qdC|h4lKiG~-Q%n-#kxaRfgvrsuu+jNzXd8ZDI~(qE zd%K4(1Lr37#j8Y=kRD5f@Xef>P`{UFqB1jM-@ZWlSujDKL-IK^7RWE~$o&eE&mzI9 zV!Gla7FS7P&U&Nw3=UjE!U_vJi7#Upv!7f+f(uU&dutULmS4jz!>}+HeK2yAetTbg z8X$op&k>w<@HX-owl5-i90^6T28d*bOU<-Q{5RV1IqO9df^52EUDoD|h{0a7TW{f< z#z_rc&gvw9HY$+dA!fP{$y!n!WACskIeK(|bNT@sd;$sP48hlq#Dk;+Nhy*}B=|mv zy&ost*uoQvy#>u%OE%j&7%*L1qcJ6^sl#Z~hy$^uIj>FRc$-b&^S|_+5!d4fsy7O} nk9WYEI}dzaf=_hW{4Rfq-{~*%dt7|=Fl+h!_WW;Ynmh6zPL_}8 delta 6647 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 WOM9F6wY^QXUT=*n|67rf4*v(MTvVX| diff --git a/data analysis/analysis/__pycache__/analysis.cpython-37.pyc b/data analysis/analysis/__pycache__/analysis.cpython-37.pyc index 32ddefec1fe6baad9ac956f9d4f3c1a9d669f4c1..88adc4a7b191c8f8e0843a334bd81922afc783cb 100644 GIT binary patch delta 7369 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 mK!PQJLS@VW6`G2+;4p;aQ?sf ZC9xz?Au%s8r?NP+I0nwxEX-mm4glj$8iW7< diff --git a/data analysis/analysis/__pycache__/trueskill.cpython-37.pyc b/data analysis/analysis/__pycache__/trueskill.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47766ae61be27efccb07763e60b0aaec1901bd6f GIT binary patch literal 32171 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 other.mu + + def __ge__(self, other): + return self.mu >= other.mu + + def __repr__(self): + return 'N(mu={:.3f}, sigma={:.3f})'.format(self.mu, self.sigma) + + def _repr_latex_(self): + latex = r'\mathcal{{ N }}( {:.3f}, {:.3f}^2 )'.format(self.mu, self.sigma) + return '$%s$' % latex + +class Matrix(list): + def __init__(self, src, height=None, width=None): + if callable(src): + f, src = src, {} + size = [height, width] + if not height: + def set_height(height): + size[0] = height + size[0] = set_height + if not width: + def set_width(width): + size[1] = width + size[1] = set_width + try: + for (r, c), val in f(*size): + src[r, c] = val + except TypeError: + raise TypeError('A callable src must return an interable ' + 'which generates a tuple containing ' + 'coordinate and value') + height, width = tuple(size) + if height is None or width is None: + raise TypeError('A callable src must call set_height and ' + 'set_width if the size is non-deterministic') + if isinstance(src, list): + is_number = lambda x: isinstance(x, Number) + unique_col_sizes = set(map(len, src)) + everything_are_number = filter(is_number, sum(src, [])) + if len(unique_col_sizes) != 1 or not everything_are_number: + raise ValueError('src must be a rectangular array of numbers') + two_dimensional_array = src + elif isinstance(src, dict): + if not height or not width: + w = h = 0 + for r, c in iterkeys(src): + if not height: + h = max(h, r + 1) + if not width: + w = max(w, c + 1) + if not height: + height = h + if not width: + width = w + two_dimensional_array = [] + for r in range(height): + row = [] + two_dimensional_array.append(row) + for c in range(width): + row.append(src.get((r, c), 0)) + else: + raise TypeError('src must be a list or dict or callable') + super(Matrix, self).__init__(two_dimensional_array) + + @property + def height(self): + return len(self) + + @property + def width(self): + return len(self[0]) + + def transpose(self): + height, width = self.height, self.width + src = {} + for c in range(width): + for r in range(height): + src[c, r] = self[r][c] + return type(self)(src, height=width, width=height) + + def minor(self, row_n, col_n): + height, width = self.height, self.width + if not (0 <= row_n < height): + raise ValueError('row_n should be between 0 and %d' % height) + elif not (0 <= col_n < width): + raise ValueError('col_n should be between 0 and %d' % width) + two_dimensional_array = [] + for r in range(height): + if r == row_n: + continue + row = [] + two_dimensional_array.append(row) + for c in range(width): + if c == col_n: + continue + row.append(self[r][c]) + return type(self)(two_dimensional_array) + + def determinant(self): + height, width = self.height, self.width + if height != width: + raise ValueError('Only square matrix can calculate a determinant') + tmp, rv = copy.deepcopy(self), 1. + for c in range(width - 1, 0, -1): + pivot, r = max((abs(tmp[r][c]), r) for r in range(c + 1)) + pivot = tmp[r][c] + if not pivot: + return 0. + tmp[r], tmp[c] = tmp[c], tmp[r] + if r != c: + rv = -rv + rv *= pivot + fact = -1. / pivot + for r in range(c): + f = fact * tmp[r][c] + for x in range(c): + tmp[r][x] += f * tmp[c][x] + return rv * tmp[0][0] + + def adjugate(self): + height, width = self.height, self.width + if height != width: + raise ValueError('Only square matrix can be adjugated') + if height == 2: + a, b = self[0][0], self[0][1] + c, d = self[1][0], self[1][1] + return type(self)([[d, -b], [-c, a]]) + src = {} + for r in range(height): + for c in range(width): + sign = -1 if (r + c) % 2 else 1 + src[r, c] = self.minor(r, c).determinant() * sign + return type(self)(src, height, width) + + def inverse(self): + if self.height == self.width == 1: + return type(self)([[1. / self[0][0]]]) + return (1. / self.determinant()) * self.adjugate() + + def __add__(self, other): + height, width = self.height, self.width + if (height, width) != (other.height, other.width): + raise ValueError('Must be same size') + src = {} + for r in range(height): + for c in range(width): + src[r, c] = self[r][c] + other[r][c] + return type(self)(src, height, width) + + def __mul__(self, other): + if self.width != other.height: + raise ValueError('Bad size') + height, width = self.height, other.width + src = {} + for r in range(height): + for c in range(width): + src[r, c] = sum(self[r][x] * other[x][c] + for x in range(self.width)) + return type(self)(src, height, width) + + def __rmul__(self, other): + if not isinstance(other, Number): + raise TypeError('The operand should be a number') + height, width = self.height, self.width + src = {} + for r in range(height): + for c in range(width): + src[r, c] = other * self[r][c] + return type(self)(src, height, width) + + def __repr__(self): + return '{}({})'.format(type(self).__name__, super(Matrix, self).__repr__()) + + def _repr_latex_(self): + rows = [' && '.join(['%.3f' % cell for cell in row]) for row in self] + latex = r'\begin{matrix} %s \end{matrix}' % r'\\'.join(rows) + return '$%s$' % latex + +def _gen_erfcinv(erfc, math=math): + def erfcinv(y): + """The inverse function of erfc.""" + if y >= 2: + return -100. + elif y <= 0: + return 100. + zero_point = y < 1 + if not zero_point: + y = 2 - y + t = math.sqrt(-2 * math.log(y / 2.)) + x = -0.70711 * \ + ((2.30753 + t * 0.27061) / (1. + t * (0.99229 + t * 0.04481)) - t) + for i in range(2): + err = erfc(x) - y + x += err / (1.12837916709551257 * math.exp(-(x ** 2)) - x * err) + return x if zero_point else -x + return erfcinv + +def _gen_ppf(erfc, math=math): + erfcinv = _gen_erfcinv(erfc, math) + def ppf(x, mu=0, sigma=1): + return mu - sigma * math.sqrt(2) * erfcinv(2 * x) + return ppf + +def erfc(x): + z = abs(x) + t = 1. / (1. + z / 2.) + r = t * math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * ( + 0.37409196 + t * (0.09678418 + t * (-0.18628806 + t * ( + 0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * ( + -0.82215223 + t * 0.17087277 + ))) + ))) + ))) + return 2. - r if x < 0 else r + +def cdf(x, mu=0, sigma=1): + return 0.5 * erfc(-(x - mu) / (sigma * math.sqrt(2))) -__all__ = [ - # TrueSkill objects - 'TrueSkill', 'Rating', - # functions for the global environment - 'rate', 'quality', 'rate_1vs1', 'quality_1vs1', 'expose', 'setup', - 'global_env', - # default values - 'MU', 'SIGMA', 'BETA', 'TAU', 'DRAW_PROBABILITY', - # draw probability helpers - 'calc_draw_probability', 'calc_draw_margin', - # deprecated features - 'transform_ratings', 'match_quality', 'dynamic_draw_probability', -] +def pdf(x, mu=0, sigma=1): + return (1 / math.sqrt(2 * math.pi) * abs(sigma) * + math.exp(-(((x - mu) / abs(sigma)) ** 2 / 2))) +ppf = _gen_ppf(erfc) + +def choose_backend(backend): + if backend is None: # fallback + return cdf, pdf, ppf + elif backend == 'mpmath': + try: + import mpmath + except ImportError: + raise ImportError('Install "mpmath" to use this backend') + return mpmath.ncdf, mpmath.npdf, _gen_ppf(mpmath.erfc, math=mpmath) + elif backend == 'scipy': + try: + from scipy.stats import norm + except ImportError: + raise ImportError('Install "scipy" to use this backend') + return norm.cdf, norm.pdf, norm.ppf + raise ValueError('%r backend is not defined' % backend) + +def available_backends(): + backends = [None] + for backend in ['mpmath', 'scipy']: + try: + __import__(backend) + except ImportError: + continue + backends.append(backend) + return backends + +class Node(object): + + pass + +class Variable(Node, Gaussian): + + def __init__(self): + self.messages = {} + super(Variable, self).__init__() + + def set(self, val): + delta = self.delta(val) + self.pi, self.tau = val.pi, val.tau + return delta + + def delta(self, other): + pi_delta = abs(self.pi - other.pi) + if pi_delta == inf: + return 0. + return max(abs(self.tau - other.tau), math.sqrt(pi_delta)) + + def update_message(self, factor, pi=0, tau=0, message=None): + message = message or Gaussian(pi=pi, tau=tau) + old_message, self[factor] = self[factor], message + return self.set(self / old_message * message) + + def update_value(self, factor, pi=0, tau=0, value=None): + value = value or Gaussian(pi=pi, tau=tau) + old_message = self[factor] + self[factor] = value * old_message / self + return self.set(value) + + def __getitem__(self, factor): + return self.messages[factor] + + def __setitem__(self, factor, message): + self.messages[factor] = message + + def __repr__(self): + args = (type(self).__name__, super(Variable, self).__repr__(), + len(self.messages), '' if len(self.messages) == 1 else 's') + return '<%s %s with %d connection%s>' % args + + +class Factor(Node): + + def __init__(self, variables): + self.vars = variables + for var in variables: + var[self] = Gaussian() + + def down(self): + return 0 + + def up(self): + return 0 + + @property + def var(self): + assert len(self.vars) == 1 + return self.vars[0] + + def __repr__(self): + args = (type(self).__name__, len(self.vars), + '' if len(self.vars) == 1 else 's') + return '<%s with %d connection%s>' % args + + +class PriorFactor(Factor): + + def __init__(self, var, val, dynamic=0): + super(PriorFactor, self).__init__([var]) + self.val = val + self.dynamic = dynamic + + def down(self): + sigma = math.sqrt(self.val.sigma ** 2 + self.dynamic ** 2) + value = Gaussian(self.val.mu, sigma) + return self.var.update_value(self, value=value) + + +class LikelihoodFactor(Factor): + + def __init__(self, mean_var, value_var, variance): + super(LikelihoodFactor, self).__init__([mean_var, value_var]) + self.mean = mean_var + self.value = value_var + self.variance = variance + + def calc_a(self, var): + return 1. / (1. + self.variance * var.pi) + + def down(self): + # update value. + msg = self.mean / self.mean[self] + a = self.calc_a(msg) + return self.value.update_message(self, a * msg.pi, a * msg.tau) + + def up(self): + # update mean. + msg = self.value / self.value[self] + a = self.calc_a(msg) + return self.mean.update_message(self, a * msg.pi, a * msg.tau) + + +class SumFactor(Factor): + + def __init__(self, sum_var, term_vars, coeffs): + super(SumFactor, self).__init__([sum_var] + term_vars) + self.sum = sum_var + self.terms = term_vars + self.coeffs = coeffs + + def down(self): + vals = self.terms + msgs = [var[self] for var in vals] + return self.update(self.sum, vals, msgs, self.coeffs) + + def up(self, index=0): + coeff = self.coeffs[index] + coeffs = [] + for x, c in enumerate(self.coeffs): + try: + if x == index: + coeffs.append(1. / coeff) + else: + coeffs.append(-c / coeff) + except ZeroDivisionError: + coeffs.append(0.) + vals = self.terms[:] + vals[index] = self.sum + msgs = [var[self] for var in vals] + return self.update(self.terms[index], vals, msgs, coeffs) + + def update(self, var, vals, msgs, coeffs): + pi_inv = 0 + mu = 0 + for val, msg, coeff in zip(vals, msgs, coeffs): + div = val / msg + mu += coeff * div.mu + if pi_inv == inf: + continue + try: + # numpy.float64 handles floating-point error by different way. + # For example, it can just warn RuntimeWarning on n/0 problem + # instead of throwing ZeroDivisionError. So div.pi, the + # denominator has to be a built-in float. + pi_inv += coeff ** 2 / float(div.pi) + except ZeroDivisionError: + pi_inv = inf + pi = 1. / pi_inv + tau = pi * mu + return var.update_message(self, pi, tau) + + +class TruncateFactor(Factor): + + def __init__(self, var, v_func, w_func, draw_margin): + super(TruncateFactor, self).__init__([var]) + self.v_func = v_func + self.w_func = w_func + self.draw_margin = draw_margin + + def up(self): + val = self.var + msg = self.var[self] + div = val / msg + sqrt_pi = math.sqrt(div.pi) + args = (div.tau / sqrt_pi, self.draw_margin * sqrt_pi) + v = self.v_func(*args) + w = self.w_func(*args) + denom = (1. - w) + pi, tau = div.pi / denom, (div.tau + sqrt_pi * v) / denom + return val.update_value(self, pi, tau) #: Default initial mean of ratings. MU = 25. @@ -54,35 +503,18 @@ DELTA = 0.0001 def calc_draw_probability(draw_margin, size, env=None): - """Calculates a draw-probability from the given ``draw_margin``. - - :param draw_margin: the draw-margin. - :param size: the number of players in two comparing teams. - :param env: the :class:`TrueSkill` object. Defaults to the global - environment. - - """ if env is None: env = global_env() return 2 * env.cdf(draw_margin / (math.sqrt(size) * env.beta)) - 1 def calc_draw_margin(draw_probability, size, env=None): - """Calculates a draw-margin from the given ``draw_probability``. - - :param draw_probability: the draw-probability. - :param size: the number of players in two comparing teams. - :param env: the :class:`TrueSkill` object. Defaults to the global - environment. - - """ if env is None: env = global_env() return env.ppf((draw_probability + 1) / 2.) * math.sqrt(size) * env.beta def _team_sizes(rating_groups): - """Makes a size map of each teams.""" team_sizes = [0] for group in rating_groups: team_sizes.append(len(group) + team_sizes[-1]) @@ -99,17 +531,6 @@ def _floating_point_error(env): class Rating(Gaussian): - """Represents a player's skill as Gaussian distrubution. - - The default mu and sigma value follows the global environment's settings. - If you don't want to use the global, use :meth:`TrueSkill.create_rating` to - create the rating object. - - :param mu: the mean. - :param sigma: the standard deviation. - - """ - def __init__(self, mu=None, sigma=None): if isinstance(mu, tuple): mu, sigma = mu @@ -140,40 +561,6 @@ class Rating(Gaussian): class TrueSkill(object): - """Implements a TrueSkill environment. An environment could have - customized constants. Every games have not same design and may need to - customize TrueSkill constants. - - For example, 60% of matches in your game have finished as draw then you - should set ``draw_probability`` to 0.60:: - - env = TrueSkill(draw_probability=0.60) - - For more details of the constants, see `The Math Behind TrueSkill`_ by - Jeff Moser. - - .. _The Math Behind TrueSkill:: http://bit.ly/trueskill-math - - :param mu: the initial mean of ratings. - :param sigma: the initial standard deviation of ratings. The recommended - value is a third of ``mu``. - :param beta: the distance which guarantees about 76% chance of winning. - The recommended value is a half of ``sigma``. - :param tau: the dynamic factor which restrains a fixation of rating. The - recommended value is ``sigma`` per cent. - :param draw_probability: the draw probability between two teams. It can be - a ``float`` or function which returns a ``float`` - by the given two rating (team performance) - arguments and the beta value. If it is a - ``float``, the game has fixed draw probability. - Otherwise, the draw probability will be decided - dynamically per each match. - :param backend: the name of a backend which implements cdf, pdf, ppf. See - :mod:`trueskill.backends` for more details. Defaults to - ``None``. - - """ - def __init__(self, mu=MU, sigma=SIGMA, beta=BETA, tau=TAU, draw_probability=DRAW_PROBABILITY, backend=None): self.mu = mu @@ -188,14 +575,6 @@ class TrueSkill(object): self.cdf, self.pdf, self.ppf = choose_backend(backend) def create_rating(self, mu=None, sigma=None): - """Initializes new :class:`Rating` object, but it fixes default mu and - sigma to the environment's. - - >>> env = TrueSkill(mu=0, sigma=1) - >>> env.create_rating() - trueskill.Rating(mu=0.000, sigma=1.000) - - """ if mu is None: mu = self.mu if sigma is None: @@ -203,15 +582,11 @@ class TrueSkill(object): return Rating(mu, sigma) def v_win(self, diff, draw_margin): - """The non-draw version of "V" function. "V" calculates a variation of - a mean. - """ x = diff - draw_margin denom = self.cdf(x) return (self.pdf(x) / denom) if denom else -x def v_draw(self, diff, draw_margin): - """The draw version of "V" function.""" abs_diff = abs(diff) a, b = draw_margin - abs_diff, -draw_margin - abs_diff denom = self.cdf(a) - self.cdf(b) @@ -219,9 +594,6 @@ class TrueSkill(object): return ((numer / denom) if denom else a) * (-1 if diff < 0 else +1) def w_win(self, diff, draw_margin): - """The non-draw version of "W" function. "W" calculates a variation of - a standard deviation. - """ x = diff - draw_margin v = self.v_win(diff, draw_margin) w = v * (v + x) @@ -230,7 +602,6 @@ class TrueSkill(object): raise _floating_point_error(self) def w_draw(self, diff, draw_margin): - """The draw version of "W" function.""" abs_diff = abs(diff) a, b = draw_margin - abs_diff, -draw_margin - abs_diff denom = self.cdf(a) - self.cdf(b) @@ -240,27 +611,6 @@ class TrueSkill(object): return (v ** 2) + (a * self.pdf(a) - b * self.pdf(b)) / denom def validate_rating_groups(self, rating_groups): - """Validates a ``rating_groups`` argument. It should contain more than - 2 groups and all groups must not be empty. - - >>> env = TrueSkill() - >>> env.validate_rating_groups([]) - Traceback (most recent call last): - ... - ValueError: need multiple rating groups - >>> env.validate_rating_groups([(Rating(),)]) - Traceback (most recent call last): - ... - ValueError: need multiple rating groups - >>> env.validate_rating_groups([(Rating(),), ()]) - Traceback (most recent call last): - ... - ValueError: each group must contain multiple ratings - >>> env.validate_rating_groups([(Rating(),), (Rating(),)]) - ... #doctest: +ELLIPSIS - [(truekill.Rating(...),), (trueskill.Rating(...),)] - - """ # check group sizes if len(rating_groups) < 2: raise ValueError('Need multiple rating groups') @@ -304,25 +654,6 @@ class TrueSkill(object): return weights def factor_graph_builders(self, rating_groups, ranks, weights): - """Makes nodes for the TrueSkill factor graph. - - Here's an example of a TrueSkill factor graph when 1 vs 2 vs 1 match:: - - rating_layer: O O O O (PriorFactor) - | | | | - | | | | - perf_layer: O O O O (LikelihoodFactor) - | \ / | - | | | - team_perf_layer: O O O (SumFactor) - \ / \ / - | | - team_diff_layer: O O (SumFactor) - | | - | | - trunc_layer: O O (TruncateFactor) - - """ flatten_ratings = sum(map(tuple, rating_groups), ()) flatten_weights = sum(map(tuple, weights), ()) size = len(flatten_ratings) @@ -379,9 +710,6 @@ class TrueSkill(object): def run_schedule(self, build_rating_layer, build_perf_layer, build_team_perf_layer, build_team_diff_layer, build_trunc_layer, min_delta=DELTA): - """Sends messages within every nodes of the factor graph until the - result is reliable. - """ if min_delta <= 0: raise ValueError('min_delta must be greater than 0') layers = [] @@ -431,49 +759,6 @@ class TrueSkill(object): return layers def rate(self, rating_groups, ranks=None, weights=None, min_delta=DELTA): - """Recalculates ratings by the ranking table:: - - env = TrueSkill() # uses default settings - # create ratings - r1 = env.create_rating(42.222) - r2 = env.create_rating(89.999) - # calculate new ratings - rating_groups = [(r1,), (r2,)] - rated_rating_groups = env.rate(rating_groups, ranks=[0, 1]) - # save new ratings - (r1,), (r2,) = rated_rating_groups - - ``rating_groups`` is a list of rating tuples or dictionaries that - represents each team of the match. You will get a result as same - structure as this argument. Rating dictionaries for this may be useful - to choose specific player's new rating:: - - # load players from the database - p1 = load_player_from_database('Arpad Emrick Elo') - p2 = load_player_from_database('Mark Glickman') - p3 = load_player_from_database('Heungsub Lee') - # calculate new ratings - rating_groups = [{p1: p1.rating, p2: p2.rating}, {p3: p3.rating}] - rated_rating_groups = env.rate(rating_groups, ranks=[0, 1]) - # save new ratings - for player in [p1, p2, p3]: - player.rating = rated_rating_groups[player.team][player] - - :param rating_groups: a list of tuples or dictionaries containing - :class:`Rating` objects. - :param ranks: a ranking table. By default, it is same as the order of - the ``rating_groups``. - :param weights: weights of each players for "partial play". - :param min_delta: each loop checks a delta of changes and the loop - will stop if the delta is less then this argument. - :returns: recalculated ratings same structure as ``rating_groups``. - :raises: :exc:`FloatingPointError` occurs when winners have too lower - rating than losers. higher floating-point precision couls - solve this error. set the backend to "mpmath". - - .. versionadded:: 0.2 - - """ rating_groups, keys = self.validate_rating_groups(rating_groups) weights = self.validate_weights(weights, rating_groups, keys) group_size = len(rating_groups) @@ -513,20 +798,6 @@ class TrueSkill(object): return [dict(zip(keys[x], g)) for x, g in unsorting] def quality(self, rating_groups, weights=None): - """Calculates the match quality of the given rating groups. A result - is the draw probability in the association:: - - env = TrueSkill() - if env.quality([team1, team2, team3]) < 0.50: - print('This match seems to be not so fair') - - :param rating_groups: a list of tuples or dictionaries containing - :class:`Rating` objects. - :param weights: weights of each players for "partial play". - - .. versionadded:: 0.2 - - """ rating_groups, keys = self.validate_rating_groups(rating_groups) weights = self.validate_weights(weights, rating_groups, keys) flatten_ratings = sum(map(tuple, rating_groups), ()) @@ -568,31 +839,10 @@ class TrueSkill(object): return math.exp(e_arg) * math.sqrt(s_arg) def expose(self, rating): - """Returns the value of the rating exposure. It starts from 0 and - converges to the mean. Use this as a sort key in a leaderboard:: - - leaderboard = sorted(ratings, key=env.expose, reverse=True) - - .. versionadded:: 0.4 - - """ k = self.mu / self.sigma return rating.mu - k * rating.sigma def make_as_global(self): - """Registers the environment as the global environment. - - >>> env = TrueSkill(mu=50) - >>> Rating() - trueskill.Rating(mu=25.000, sigma=8.333) - >>> env.make_as_global() #doctest: +ELLIPSIS - trueskill.TrueSkill(mu=50.000, ...) - >>> Rating() - trueskill.Rating(mu=50.000, sigma=8.333) - - But if you need just one environment, :func:`setup` is better to use. - - """ return setup(env=self) def __repr__(self): @@ -615,24 +865,6 @@ class TrueSkill(object): def rate_1vs1(rating1, rating2, drawn=False, min_delta=DELTA, env=None): - """A shortcut to rate just 2 players in a head-to-head match:: - - alice, bob = Rating(25), Rating(30) - alice, bob = rate_1vs1(alice, bob) - alice, bob = rate_1vs1(alice, bob, drawn=True) - - :param rating1: the winner's rating if they didn't draw. - :param rating2: the loser's rating if they didn't draw. - :param drawn: if the players drew, set this to ``True``. Defaults to - ``False``. - :param min_delta: will be passed to :meth:`rate`. - :param env: the :class:`TrueSkill` object. Defaults to the global - environment. - :returns: a tuple containing recalculated 2 ratings. - - .. versionadded:: 0.2 - - """ if env is None: env = global_env() ranks = [0, 0 if drawn else 1] @@ -641,27 +873,12 @@ def rate_1vs1(rating1, rating2, drawn=False, min_delta=DELTA, env=None): def quality_1vs1(rating1, rating2, env=None): - """A shortcut to calculate the match quality between just 2 players in - a head-to-head match:: - - if quality_1vs1(alice, bob) < 0.50: - print('This match seems to be not so fair') - - :param rating1: the rating. - :param rating2: the another rating. - :param env: the :class:`TrueSkill` object. Defaults to the global - environment. - - .. versionadded:: 0.2 - - """ if env is None: env = global_env() return env.quality([(rating1,), (rating2,)]) def global_env(): - """Gets the :class:`TrueSkill` object which is the global environment.""" try: global_env.__trueskill__ except AttributeError: @@ -672,19 +889,6 @@ def global_env(): def setup(mu=MU, sigma=SIGMA, beta=BETA, tau=TAU, draw_probability=DRAW_PROBABILITY, backend=None, env=None): - """Setups the global environment. - - :param env: the specific :class:`TrueSkill` object to be the global - environment. It is optional. - - >>> Rating() - trueskill.Rating(mu=25.000, sigma=8.333) - >>> setup(mu=50) #doctest: +ELLIPSIS - trueskill.TrueSkill(mu=50.000, ...) - >>> Rating() - trueskill.Rating(mu=50.000, sigma=8.333) - - """ if env is None: env = TrueSkill(mu, sigma, beta, tau, draw_probability, backend) global_env.__trueskill__ = env @@ -692,35 +896,12 @@ def setup(mu=MU, sigma=SIGMA, beta=BETA, tau=TAU, def rate(rating_groups, ranks=None, weights=None, min_delta=DELTA): - """A proxy function for :meth:`TrueSkill.rate` of the global environment. - - .. versionadded:: 0.2 - - """ return global_env().rate(rating_groups, ranks, weights, min_delta) def quality(rating_groups, weights=None): - """A proxy function for :meth:`TrueSkill.quality` of the global - environment. - - .. versionadded:: 0.2 - - """ return global_env().quality(rating_groups, weights) def expose(rating): - """A proxy function for :meth:`TrueSkill.expose` of the global environment. - - .. versionadded:: 0.4 - - """ - return global_env().expose(rating) - - -# Append deprecated methods into :class:`TrueSkill` and :class:`Rating` -from . import deprecated # noqa -from .deprecated import ( # noqa - dynamic_draw_probability, match_quality, transform_ratings) -deprecated.ensure_backward_compatibility(TrueSkill, Rating) + return global_env().expose(rating) \ No newline at end of file diff --git a/data analysis/analysis/trueskill/__about__.py b/data analysis/analysis/trueskill/__about__.py deleted file mode 100644 index 8e5e3ee3..00000000 --- a/data analysis/analysis/trueskill/__about__.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -""" - trueskill.__about__ - ~~~~~~~~~~~~~~~~~~~ -""" -__version__ = '0.4.5' -__license__ = 'BSD' -__author__ = 'Heungsub Lee' -__author_email__ = 'sub@subl.ee' -__url__ = 'http://trueskill.org/' -__description__ = 'The video game rating system' diff --git a/data analysis/analysis/trueskill/__pycache__/__about__.cpython-37.pyc b/data analysis/analysis/trueskill/__pycache__/__about__.cpython-37.pyc deleted file mode 100644 index 711958bc8bae223e1f685441a3322d3196351fc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 414 zcmZWlyH3ME5IiSw5;=*Wp`*AqMQjRaB7~xdA_bxe)k)~&_*V9bv(LImq`2TW_y)d& ze@M$;P_YgoL1I=rn%QEsn&o5?5yZ#p?Rgjw^4XeSpeGAlOOXU&Lo@cP zpc$c9;HntSMd4gMPm)u#HhI!qJ{3T>Qh=s;S^_nxlT}Xb&N?U`r-S`~bD=ZN_an}? zV64>Dk5Mv%$^x1QpY*QK2KD(-Lzzm27Yy*G6=#?PHZ!tzhmdz-hS!gz*UT~UkwNik7 diff --git a/data analysis/analysis/trueskill/__pycache__/__init__.cpython-37.pyc b/data analysis/analysis/trueskill/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 96fe00953bb09751c9884be5fc6df10bb4eb2af2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25214 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 diff --git a/data analysis/analysis/trueskill/__pycache__/backends.cpython-37.pyc b/data analysis/analysis/trueskill/__pycache__/backends.cpython-37.pyc deleted file mode 100644 index 1fb6a2f2e8eb2213fefc0e57792ae44e86ddc48f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4073 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 diff --git a/data analysis/analysis/trueskill/backends.py b/data analysis/analysis/trueskill/backends.py deleted file mode 100644 index 0fc03754..00000000 --- a/data analysis/analysis/trueskill/backends.py +++ /dev/null @@ -1,135 +0,0 @@ -# -*- coding: utf-8 -*- -""" - trueskill.backends - ~~~~~~~~~~~~~~~~~~ - - Provides mathematical statistics backend chooser. - - :copyright: (c) 2012-2016 by Heungsub Lee. - :license: BSD, see LICENSE for more details. - -""" -from __future__ import absolute_import - -import math - -from six.moves import range - - -__all__ = ['available_backends', 'choose_backend', 'cdf', 'pdf', 'ppf'] - - -def _gen_erfcinv(erfc, math=math): - """Generates the inverse function of erfc by the given erfc function and - math module. - """ - def erfcinv(y): - """The inverse function of erfc.""" - if y >= 2: - return -100. - elif y <= 0: - return 100. - zero_point = y < 1 - if not zero_point: - y = 2 - y - t = math.sqrt(-2 * math.log(y / 2.)) - x = -0.70711 * \ - ((2.30753 + t * 0.27061) / (1. + t * (0.99229 + t * 0.04481)) - t) - for i in range(2): - err = erfc(x) - y - x += err / (1.12837916709551257 * math.exp(-(x ** 2)) - x * err) - return x if zero_point else -x - return erfcinv - - -def _gen_ppf(erfc, math=math): - """ppf is the inverse function of cdf. This function generates cdf by the - given erfc and math module. - """ - erfcinv = _gen_erfcinv(erfc, math) - def ppf(x, mu=0, sigma=1): - """The inverse function of cdf.""" - return mu - sigma * math.sqrt(2) * erfcinv(2 * x) - return ppf - - -def erfc(x): - """Complementary error function (via `http://bit.ly/zOLqbc`_)""" - z = abs(x) - t = 1. / (1. + z / 2.) - r = t * math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * ( - 0.37409196 + t * (0.09678418 + t * (-0.18628806 + t * ( - 0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * ( - -0.82215223 + t * 0.17087277 - ))) - ))) - ))) - return 2. - r if x < 0 else r - - -def cdf(x, mu=0, sigma=1): - """Cumulative distribution function""" - return 0.5 * erfc(-(x - mu) / (sigma * math.sqrt(2))) - - -def pdf(x, mu=0, sigma=1): - """Probability density function""" - return (1 / math.sqrt(2 * math.pi) * abs(sigma) * - math.exp(-(((x - mu) / abs(sigma)) ** 2 / 2))) - - -ppf = _gen_ppf(erfc) - - -def choose_backend(backend): - """Returns a tuple containing cdf, pdf, ppf from the chosen backend. - - >>> cdf, pdf, ppf = choose_backend(None) - >>> cdf(-10) - 7.619853263532764e-24 - >>> cdf, pdf, ppf = choose_backend('mpmath') - >>> cdf(-10) - mpf('7.6198530241605255e-24') - - .. versionadded:: 0.3 - - """ - if backend is None: # fallback - return cdf, pdf, ppf - elif backend == 'mpmath': - try: - import mpmath - except ImportError: - raise ImportError('Install "mpmath" to use this backend') - return mpmath.ncdf, mpmath.npdf, _gen_ppf(mpmath.erfc, math=mpmath) - elif backend == 'scipy': - try: - from scipy.stats import norm - except ImportError: - raise ImportError('Install "scipy" to use this backend') - return norm.cdf, norm.pdf, norm.ppf - raise ValueError('%r backend is not defined' % backend) - - -def available_backends(): - """Detects list of available backends. All of defined backends are - ``None`` -- internal implementation, "mpmath", "scipy". - - You can check if the backend is available in the current environment with - this function:: - - if 'mpmath' in available_backends(): - # mpmath can be used in the current environment - setup(backend='mpmath') - - .. versionadded:: 0.3 - - """ - backends = [None] - for backend in ['mpmath', 'scipy']: - try: - __import__(backend) - except ImportError: - continue - backends.append(backend) - return backends diff --git a/data analysis/analysis/trueskill/deprecated.py b/data analysis/analysis/trueskill/deprecated.py deleted file mode 100644 index 6378f169..00000000 --- a/data analysis/analysis/trueskill/deprecated.py +++ /dev/null @@ -1,134 +0,0 @@ -# -*- coding: utf-8 -*- -""" - trueskill.deprecated - ~~~~~~~~~~~~~~~~~~~~ - - Deprecated features. - - :copyright: (c) 2012-2016 by Heungsub Lee - :license: BSD, see LICENSE for more details. - -""" -from __future__ import absolute_import - -import warnings - -from . import DELTA, expose, global_env, quality_1vs1, rate_1vs1, Rating - - -__all__ = ['transform_ratings', 'match_quality', 'dynamic_draw_probability', - 'ensure_backward_compatibility'] - - -# deprecated functions - - -def transform_ratings(rating_groups, ranks=None, min_delta=DELTA): - return global_env().transform_ratings(rating_groups, ranks, min_delta) - - -def match_quality(rating_groups): - return global_env().match_quality(rating_groups) - - -def dynamic_draw_probability(rating1, rating2, env=None): - """Deprecated. It was an approximation for :func:`quality_1vs1`. - - .. deprecated:: 0.4.1 - Use :func:`quality_1vs1` instead. - - """ - warnings.warn('Use quality_1vs1() instead', DeprecationWarning) - return quality_1vs1(rating1, rating2, env=env) - - -# deprecated methods - - -def addattr(obj, attr, value): - if hasattr(obj, attr): - raise AttributeError('The attribute already exists') - return setattr(obj, attr, value) - - -def ensure_backward_compatibility(TrueSkill, Rating): - addattr(TrueSkill, 'Rating', TrueSkill_Rating) - addattr(TrueSkill, 'transform_ratings', TrueSkill_transform_ratings) - addattr(TrueSkill, 'match_quality', TrueSkill_match_quality) - addattr(TrueSkill, 'rate_1vs1', TrueSkill_rate_1vs1) - addattr(TrueSkill, 'quality_1vs1', TrueSkill_quality_1vs1) - addattr(Rating, 'exposure', Rating_exposure) - - -def TrueSkill_Rating(self, mu=None, sigma=None): - """Deprecated. Used to create a :class:`Rating` object. - - .. deprecated:: 0.2 - Override :meth:`create_rating` instead. - - """ - warnings.warn('Use TrueSkill.create_rating() instead', DeprecationWarning) - return self.create_rating(mu, sigma) - - -def TrueSkill_transform_ratings(self, rating_groups, ranks=None, - min_delta=DELTA): - """Deprecated. Used to rate the given ratings. - - .. deprecated:: 0.2 - Override :meth:`rate` instead. - - """ - warnings.warn('Use TrueSkill.rate() instead', DeprecationWarning) - rating_groups = [(r,) if isinstance(r, Rating) else r - for r in rating_groups] - return self.rate(rating_groups, ranks, min_delta=min_delta) - - -def TrueSkill_match_quality(self, rating_groups): - """Deprecated. Used to calculate a match quality. - - .. deprecated:: 0.2 - Override :meth:`quality` instead. - - """ - warnings.warn('Use TrueSkill.quality() instead', DeprecationWarning) - rating_groups = [(r,) if isinstance(r, Rating) else r - for r in rating_groups] - return self.quality(rating_groups) - - -def TrueSkill_rate_1vs1(self, rating1, rating2, drawn=False, min_delta=DELTA): - """Deprecated. Used to rate just a head-to-haed match. - - .. deprecated:: 0.4 - Use :func:`rate_1vs1` instead. - - """ - warnings.warn('Use global function rate_1vs1() instead', - DeprecationWarning) - return rate_1vs1(rating1, rating2, drawn, min_delta, self) - - -def TrueSkill_quality_1vs1(self, rating1, rating2): - """Deprecated. Used to calculate a match quality for a head-to-haed match. - - .. deprecated:: 0.4 - Use :func:`quality_1vs1` instead. - - """ - warnings.warn('Use global function quality_1vs1() instead', - DeprecationWarning) - return quality_1vs1(rating1, rating2, self) - - -@property -def Rating_exposure(self): - """Deprecated. Used to get a value that will go up on the whole. - - .. deprecated:: 0.4 - Use :meth:`TrueSkill.expose` instead. - - """ - warnings.warn('Use TrueSkill.expose() instead', DeprecationWarning) - return expose(self) diff --git a/data analysis/analysis/trueskill/factorgraph.py b/data analysis/analysis/trueskill/factorgraph.py deleted file mode 100644 index 569d522c..00000000 --- a/data analysis/analysis/trueskill/factorgraph.py +++ /dev/null @@ -1,199 +0,0 @@ -# -*- coding: utf-8 -*- -""" - trueskill.factorgraph - ~~~~~~~~~~~~~~~~~~~~~ - - This module contains nodes for the factor graph of TrueSkill algorithm. - - :copyright: (c) 2012-2016 by Heungsub Lee. - :license: BSD, see LICENSE for more details. - -""" -from __future__ import absolute_import - -import math - -from six.moves import zip - -from .mathematics import Gaussian, inf - - -__all__ = ['Variable', 'PriorFactor', 'LikelihoodFactor', 'SumFactor', - 'TruncateFactor'] - - -class Node(object): - - pass - - -class Variable(Node, Gaussian): - - def __init__(self): - self.messages = {} - super(Variable, self).__init__() - - def set(self, val): - delta = self.delta(val) - self.pi, self.tau = val.pi, val.tau - return delta - - def delta(self, other): - pi_delta = abs(self.pi - other.pi) - if pi_delta == inf: - return 0. - return max(abs(self.tau - other.tau), math.sqrt(pi_delta)) - - def update_message(self, factor, pi=0, tau=0, message=None): - message = message or Gaussian(pi=pi, tau=tau) - old_message, self[factor] = self[factor], message - return self.set(self / old_message * message) - - def update_value(self, factor, pi=0, tau=0, value=None): - value = value or Gaussian(pi=pi, tau=tau) - old_message = self[factor] - self[factor] = value * old_message / self - return self.set(value) - - def __getitem__(self, factor): - return self.messages[factor] - - def __setitem__(self, factor, message): - self.messages[factor] = message - - def __repr__(self): - args = (type(self).__name__, super(Variable, self).__repr__(), - len(self.messages), '' if len(self.messages) == 1 else 's') - return '<%s %s with %d connection%s>' % args - - -class Factor(Node): - - def __init__(self, variables): - self.vars = variables - for var in variables: - var[self] = Gaussian() - - def down(self): - return 0 - - def up(self): - return 0 - - @property - def var(self): - assert len(self.vars) == 1 - return self.vars[0] - - def __repr__(self): - args = (type(self).__name__, len(self.vars), - '' if len(self.vars) == 1 else 's') - return '<%s with %d connection%s>' % args - - -class PriorFactor(Factor): - - def __init__(self, var, val, dynamic=0): - super(PriorFactor, self).__init__([var]) - self.val = val - self.dynamic = dynamic - - def down(self): - sigma = math.sqrt(self.val.sigma ** 2 + self.dynamic ** 2) - value = Gaussian(self.val.mu, sigma) - return self.var.update_value(self, value=value) - - -class LikelihoodFactor(Factor): - - def __init__(self, mean_var, value_var, variance): - super(LikelihoodFactor, self).__init__([mean_var, value_var]) - self.mean = mean_var - self.value = value_var - self.variance = variance - - def calc_a(self, var): - return 1. / (1. + self.variance * var.pi) - - def down(self): - # update value. - msg = self.mean / self.mean[self] - a = self.calc_a(msg) - return self.value.update_message(self, a * msg.pi, a * msg.tau) - - def up(self): - # update mean. - msg = self.value / self.value[self] - a = self.calc_a(msg) - return self.mean.update_message(self, a * msg.pi, a * msg.tau) - - -class SumFactor(Factor): - - def __init__(self, sum_var, term_vars, coeffs): - super(SumFactor, self).__init__([sum_var] + term_vars) - self.sum = sum_var - self.terms = term_vars - self.coeffs = coeffs - - def down(self): - vals = self.terms - msgs = [var[self] for var in vals] - return self.update(self.sum, vals, msgs, self.coeffs) - - def up(self, index=0): - coeff = self.coeffs[index] - coeffs = [] - for x, c in enumerate(self.coeffs): - try: - if x == index: - coeffs.append(1. / coeff) - else: - coeffs.append(-c / coeff) - except ZeroDivisionError: - coeffs.append(0.) - vals = self.terms[:] - vals[index] = self.sum - msgs = [var[self] for var in vals] - return self.update(self.terms[index], vals, msgs, coeffs) - - def update(self, var, vals, msgs, coeffs): - pi_inv = 0 - mu = 0 - for val, msg, coeff in zip(vals, msgs, coeffs): - div = val / msg - mu += coeff * div.mu - if pi_inv == inf: - continue - try: - # numpy.float64 handles floating-point error by different way. - # For example, it can just warn RuntimeWarning on n/0 problem - # instead of throwing ZeroDivisionError. So div.pi, the - # denominator has to be a built-in float. - pi_inv += coeff ** 2 / float(div.pi) - except ZeroDivisionError: - pi_inv = inf - pi = 1. / pi_inv - tau = pi * mu - return var.update_message(self, pi, tau) - - -class TruncateFactor(Factor): - - def __init__(self, var, v_func, w_func, draw_margin): - super(TruncateFactor, self).__init__([var]) - self.v_func = v_func - self.w_func = w_func - self.draw_margin = draw_margin - - def up(self): - val = self.var - msg = self.var[self] - div = val / msg - sqrt_pi = math.sqrt(div.pi) - args = (div.tau / sqrt_pi, self.draw_margin * sqrt_pi) - v = self.v_func(*args) - w = self.w_func(*args) - denom = (1. - w) - pi, tau = div.pi / denom, (div.tau + sqrt_pi * v) / denom - return val.update_value(self, pi, tau) diff --git a/data analysis/analysis/trueskill/mathematics.py b/data analysis/analysis/trueskill/mathematics.py deleted file mode 100644 index 61845137..00000000 --- a/data analysis/analysis/trueskill/mathematics.py +++ /dev/null @@ -1,260 +0,0 @@ -# -*- coding: utf-8 -*- -""" - trueskill.mathematics - ~~~~~~~~~~~~~~~~~~~~~ - - This module contains basic mathematics functions and objects for TrueSkill - algorithm. If you have not scipy, this module provides the fallback. - - :copyright: (c) 2012-2016 by Heungsub Lee. - :license: BSD, see LICENSE for more details. - -""" -from __future__ import absolute_import - -import copy -import math -try: - from numbers import Number -except ImportError: - Number = (int, long, float, complex) - -from six import iterkeys - - -__all__ = ['Gaussian', 'Matrix', 'inf'] - - -inf = float('inf') - - -class Gaussian(object): - """A model for the normal distribution.""" - - #: Precision, the inverse of the variance. - pi = 0 - #: Precision adjusted mean, the precision multiplied by the mean. - tau = 0 - - def __init__(self, mu=None, sigma=None, pi=0, tau=0): - if mu is not None: - if sigma is None: - raise TypeError('sigma argument is needed') - elif sigma == 0: - raise ValueError('sigma**2 should be greater than 0') - pi = sigma ** -2 - tau = pi * mu - self.pi = pi - self.tau = tau - - @property - def mu(self): - """A property which returns the mean.""" - return self.pi and self.tau / self.pi - - @property - def sigma(self): - """A property which returns the the square root of the variance.""" - return math.sqrt(1 / self.pi) if self.pi else inf - - def __mul__(self, other): - pi, tau = self.pi + other.pi, self.tau + other.tau - return Gaussian(pi=pi, tau=tau) - - def __truediv__(self, other): - pi, tau = self.pi - other.pi, self.tau - other.tau - return Gaussian(pi=pi, tau=tau) - - __div__ = __truediv__ # for Python 2 - - def __eq__(self, other): - return self.pi == other.pi and self.tau == other.tau - - def __lt__(self, other): - return self.mu < other.mu - - def __le__(self, other): - return self.mu <= other.mu - - def __gt__(self, other): - return self.mu > other.mu - - def __ge__(self, other): - return self.mu >= other.mu - - def __repr__(self): - return 'N(mu={:.3f}, sigma={:.3f})'.format(self.mu, self.sigma) - - def _repr_latex_(self): - latex = r'\mathcal{{ N }}( {:.3f}, {:.3f}^2 )'.format(self.mu, self.sigma) - return '$%s$' % latex - - -class Matrix(list): - """A model for matrix.""" - - def __init__(self, src, height=None, width=None): - if callable(src): - f, src = src, {} - size = [height, width] - if not height: - def set_height(height): - size[0] = height - size[0] = set_height - if not width: - def set_width(width): - size[1] = width - size[1] = set_width - try: - for (r, c), val in f(*size): - src[r, c] = val - except TypeError: - raise TypeError('A callable src must return an interable ' - 'which generates a tuple containing ' - 'coordinate and value') - height, width = tuple(size) - if height is None or width is None: - raise TypeError('A callable src must call set_height and ' - 'set_width if the size is non-deterministic') - if isinstance(src, list): - is_number = lambda x: isinstance(x, Number) - unique_col_sizes = set(map(len, src)) - everything_are_number = filter(is_number, sum(src, [])) - if len(unique_col_sizes) != 1 or not everything_are_number: - raise ValueError('src must be a rectangular array of numbers') - two_dimensional_array = src - elif isinstance(src, dict): - if not height or not width: - w = h = 0 - for r, c in iterkeys(src): - if not height: - h = max(h, r + 1) - if not width: - w = max(w, c + 1) - if not height: - height = h - if not width: - width = w - two_dimensional_array = [] - for r in range(height): - row = [] - two_dimensional_array.append(row) - for c in range(width): - row.append(src.get((r, c), 0)) - else: - raise TypeError('src must be a list or dict or callable') - super(Matrix, self).__init__(two_dimensional_array) - - @property - def height(self): - return len(self) - - @property - def width(self): - return len(self[0]) - - def transpose(self): - height, width = self.height, self.width - src = {} - for c in range(width): - for r in range(height): - src[c, r] = self[r][c] - return type(self)(src, height=width, width=height) - - def minor(self, row_n, col_n): - height, width = self.height, self.width - if not (0 <= row_n < height): - raise ValueError('row_n should be between 0 and %d' % height) - elif not (0 <= col_n < width): - raise ValueError('col_n should be between 0 and %d' % width) - two_dimensional_array = [] - for r in range(height): - if r == row_n: - continue - row = [] - two_dimensional_array.append(row) - for c in range(width): - if c == col_n: - continue - row.append(self[r][c]) - return type(self)(two_dimensional_array) - - def determinant(self): - height, width = self.height, self.width - if height != width: - raise ValueError('Only square matrix can calculate a determinant') - tmp, rv = copy.deepcopy(self), 1. - for c in range(width - 1, 0, -1): - pivot, r = max((abs(tmp[r][c]), r) for r in range(c + 1)) - pivot = tmp[r][c] - if not pivot: - return 0. - tmp[r], tmp[c] = tmp[c], tmp[r] - if r != c: - rv = -rv - rv *= pivot - fact = -1. / pivot - for r in range(c): - f = fact * tmp[r][c] - for x in range(c): - tmp[r][x] += f * tmp[c][x] - return rv * tmp[0][0] - - def adjugate(self): - height, width = self.height, self.width - if height != width: - raise ValueError('Only square matrix can be adjugated') - if height == 2: - a, b = self[0][0], self[0][1] - c, d = self[1][0], self[1][1] - return type(self)([[d, -b], [-c, a]]) - src = {} - for r in range(height): - for c in range(width): - sign = -1 if (r + c) % 2 else 1 - src[r, c] = self.minor(r, c).determinant() * sign - return type(self)(src, height, width) - - def inverse(self): - if self.height == self.width == 1: - return type(self)([[1. / self[0][0]]]) - return (1. / self.determinant()) * self.adjugate() - - def __add__(self, other): - height, width = self.height, self.width - if (height, width) != (other.height, other.width): - raise ValueError('Must be same size') - src = {} - for r in range(height): - for c in range(width): - src[r, c] = self[r][c] + other[r][c] - return type(self)(src, height, width) - - def __mul__(self, other): - if self.width != other.height: - raise ValueError('Bad size') - height, width = self.height, other.width - src = {} - for r in range(height): - for c in range(width): - src[r, c] = sum(self[r][x] * other[x][c] - for x in range(self.width)) - return type(self)(src, height, width) - - def __rmul__(self, other): - if not isinstance(other, Number): - raise TypeError('The operand should be a number') - height, width = self.height, self.width - src = {} - for r in range(height): - for c in range(width): - src[r, c] = other * self[r][c] - return type(self)(src, height, width) - - def __repr__(self): - return '{}({})'.format(type(self).__name__, super(Matrix, self).__repr__()) - - def _repr_latex_(self): - rows = [' && '.join(['%.3f' % cell for cell in row]) for row in self] - latex = r'\begin{matrix} %s \end{matrix}' % r'\\'.join(rows) - return '$%s$' % latex