From 2730e4fc914498ea72ba337c95fa0e5d472f6eb7 Mon Sep 17 00:00:00 2001 From: Arthur Lu Date: Wed, 20 May 2020 08:52:38 -0500 Subject: [PATCH] Merge service-dev changes with master (#24) * added config.json removed old config files Signed-off-by: Arthur * superscript.py v 0.0.6.000 Signed-off-by: Arthur * changed data.py Signed-off-by: Arthur * changes to config.json Signed-off-by: Arthur * removed cells from visualize_pit.py Signed-off-by: Arthur * more changes to visualize_pit.py Signed-off-by: Arthur * added analysis-master/metrics/__pycache__ to git ignore moved pit configs in config.json to the borrom superscript.py v 0.0.6.001 Signed-off-by: Arthur * removed old database key Signed-off-by: Arthur * adjusted config files Signed-off-by: Arthur * Delete config-pop.json * fixed .gitignore Signed-off-by: Arthur * analysis.py 1.2.1.003 added team kv pair to config.json Signed-off-by: Arthur * superscript.py v 0.0.6.002 Signed-off-by: Arthur * finished app.py API made minute changes to parentheses use in various packages Signed-off-by: Arthur Lu * bug fixes in app.py Signed-off-by: Arthur Lu * bug fixes in app.py Signed-off-by: Arthur Lu * made changes to .gitignore Signed-off-by: Arthur Lu * made changes to .gitignore Signed-off-by: Arthur Lu * deleted a __pycache__ folder from metrics Signed-off-by: Arthur Lu * more changes to .gitignore Signed-off-by: Arthur Lu * additions to app.py Signed-off-by: Arthur Lu * renamed app.py to api.py Signed-off-by: Arthur Lu * removed extranneous files Signed-off-by: Arthur Lu * renamed api.py to tra.py removed rest api calls from tra.py * renamed api.py to tra.py removed rest api calls from tra.py Signed-off-by: Arthur Lu * removed flask import from tra.py Signed-off-by: Arthur Lu * changes to devcontainer.json Signed-off-by: Arthur Lu * fixed unit tests to be correct removed some tests regressions because of potential function overflow removed trueskill unit test because of slight deviation chance Signed-off-by: Arthur Lu --- .devcontainer/devcontainer.json | 2 +- .gitignore | 2 + analysis-master/analysis/analysis.py | 7 +- .../__pycache__/__init__.cpython-37.pyc | Bin 159 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 178 -> 0 bytes .../metrics/__pycache__/elo.cpython-37.pyc | Bin 410 -> 0 bytes .../metrics/__pycache__/elo.cpython-38.pyc | Bin 433 -> 0 bytes .../__pycache__/glicko2.cpython-37.pyc | Bin 3693 -> 0 bytes .../__pycache__/glicko2.cpython-38.pyc | Bin 3720 -> 0 bytes .../__pycache__/trueskill.cpython-37.pyc | Bin 32177 -> 0 bytes .../__pycache__/trueskill.cpython-38.pyc | Bin 32198 -> 0 bytes analysis-master/test_analysis.py | 13 +- data-analysis/__pycache__/data.cpython-37.pyc | Bin 4239 -> 0 bytes data-analysis/config.json | 45 +++ data-analysis/config/competition.config | 1 - data-analysis/config/database.config | 0 data-analysis/config/keys.config | 2 - data-analysis/config/stats.config | 14 - data-analysis/data.py | 53 ++- data-analysis/get_team_rankings.py | 59 --- data-analysis/superscript.py | 349 ++++++++++-------- data-analysis/tra.py | 91 +++++ data-analysis/visualize_pit.py | 59 --- 23 files changed, 378 insertions(+), 319 deletions(-) delete mode 100644 analysis-master/analysis/metrics/__pycache__/__init__.cpython-37.pyc delete mode 100644 analysis-master/analysis/metrics/__pycache__/__init__.cpython-38.pyc delete mode 100644 analysis-master/analysis/metrics/__pycache__/elo.cpython-37.pyc delete mode 100644 analysis-master/analysis/metrics/__pycache__/elo.cpython-38.pyc delete mode 100644 analysis-master/analysis/metrics/__pycache__/glicko2.cpython-37.pyc delete mode 100644 analysis-master/analysis/metrics/__pycache__/glicko2.cpython-38.pyc delete mode 100644 analysis-master/analysis/metrics/__pycache__/trueskill.cpython-37.pyc delete mode 100644 analysis-master/analysis/metrics/__pycache__/trueskill.cpython-38.pyc delete mode 100644 data-analysis/__pycache__/data.cpython-37.pyc create mode 100644 data-analysis/config.json delete mode 100644 data-analysis/config/competition.config delete mode 100644 data-analysis/config/database.config delete mode 100644 data-analysis/config/keys.config delete mode 100644 data-analysis/config/stats.config delete mode 100644 data-analysis/get_team_rankings.py create mode 100644 data-analysis/tra.py delete mode 100644 data-analysis/visualize_pit.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6f6cd0c9..704a4eab 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -23,5 +23,5 @@ "mhutchie.git-graph", "donjayamanne.jupyter", ], - "postCreateCommand": "pip install -r analysis-master/analysis-amd64/requirements.txt" + "postCreateCommand": "pip install -r analysis-master/requirements.txt" } \ No newline at end of file diff --git a/.gitignore b/.gitignore index d8734156..8e0a337d 100644 --- a/.gitignore +++ b/.gitignore @@ -21,11 +21,13 @@ data-analysis/test.ipynb data-analysis/visualize_pit.ipynb data-analysis/config/keys.config analysis-master/analysis/__pycache__/ +analysis-master/analysis/metrics/__pycache__/ data-analysis/__pycache__/ analysis-master/analysis.egg-info/ analysis-master/build/ analysis-master/metrics/ data-analysis/config-pop.json +data-analysis/__pycache__/ analysis-master/__pycache__/ analysis-master/.pytest_cache/ data-analysis/.pytest_cache/ \ No newline at end of file diff --git a/analysis-master/analysis/analysis.py b/analysis-master/analysis/analysis.py index 538d2e7c..54740484 100644 --- a/analysis-master/analysis/analysis.py +++ b/analysis-master/analysis/analysis.py @@ -12,6 +12,7 @@ __version__ = "1.2.1.003" # changelog should be viewed using print(analysis.__changelog__) __changelog__ = """changelog: 1.2.1.003: + - changed output of basic_stats and histo_analysis to libraries - fixed __all__ 1.2.1.002: - renamed ArrayTest class to Array @@ -360,7 +361,7 @@ def basic_stats(data): _min = npmin(data_t) _max = npmax(data_t) - return _mean, _median, _stdev, _variance, _min, _max + return {"mean": _mean, "median": _median, "standard-deviation": _stdev, "variance": _variance, "minimum": _min, "maximum": _max} # returns z score with inputs of point, mean and standard deviation of spread @jit(forceobj=True) @@ -383,7 +384,7 @@ def z_normalize(array, *args): # expects 2d array of [x,y] def histo_analysis(hist_data): - if(len(hist_data[0]) > 2): + if len(hist_data[0]) > 2: hist_data = np.array(hist_data) derivative = np.array(len(hist_data) - 1, dtype = float) @@ -391,7 +392,7 @@ def histo_analysis(hist_data): derivative = t[1] / t[0] np.sort(derivative) - return basic_stats(derivative)[0], basic_stats(derivative)[3] + return {"mean": basic_stats(derivative)["mean"], "deviation": basic_stats(derivative)["standard-deviation"]} else: diff --git a/analysis-master/analysis/metrics/__pycache__/__init__.cpython-37.pyc b/analysis-master/analysis/metrics/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 2e4375f0a62acef7c42deecba4d1160b00c99e81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159 zcmZ?b<>g`kf{Kr8;z0Cc5CH>>K!yVl7qb9~6oz01O-8?!3`HPe1o6wm)hZ^SC_gJT zxuiIzq{zs?$Vj)iq$sf@HN7$>F)uNvvN*F?H#f1kB((_6kI79fDauSPj){-Y%*!l^ ZkJl@xyv1RYo1apelWGUD;WH33004@IDY^gv diff --git a/analysis-master/analysis/metrics/__pycache__/__init__.cpython-38.pyc b/analysis-master/analysis/metrics/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index c4cbc91bcb4adf11ec397079b38036bfd6d519db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178 zcmWIL<>g`k0;?6v<3RLd5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_`etCXTc5y*s za%!=DNs*C(k&$k3Nl{`+YI>!9VqRiSWpQS)Zf;_6Noo;-pO~9sW&-Ex=cbkvWhNKv f$H!;pWtPOp>lIYq;;_lhPbtkwwF6oB8HgDG>bRu zf*rQae8a%YyzrqyRFw~?%!X~>C8y9W^Ti`27LD)PqOkN=NA`&fz8LMr%!}K3WLAYq zoY5P=BR2h>-b~^DRAQHz#}4x{D=KW-yVmaJf6!sUM;DpZWoj;)2UGLyk^BLZd}Xcx diff --git a/analysis-master/analysis/metrics/__pycache__/elo.cpython-38.pyc b/analysis-master/analysis/metrics/__pycache__/elo.cpython-38.pyc deleted file mode 100644 index 68622025c6bd76135a16f92b4c0d560201e57983..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 433 zcmYjO!Ab)$5S?VZWfj|t6$=ILJ=B#V9z;a&DDQ>okjbhG>&*|bC(%#J z)l+}LlanI06XreM40)L$x1G*5Q0#qu+%1W}gV+pC;(`);1QI06VagS;H2rfj%_R0Cr&?2Jjj;(6p$>8@^yGju)&CJ?fHw zG+8ooScW-=k>{oHv9MOuKEkRPwtW|!ux?(??lIF=`K~rb<60;B0hN7H(zA(YSJOnj z7?n9CKf&&h>CbigtnDKjkts}_9UqOyBXm|cHLFumioC`-jtYTJ*|iphJURZCX`!5* zXPBy7ADY^4Wg^e2oIE0W0$T*Y54i0T(-v`A6{fbKyeh2**?@_H?2q9m G68;OLx@qnJ diff --git a/analysis-master/analysis/metrics/__pycache__/glicko2.cpython-37.pyc b/analysis-master/analysis/metrics/__pycache__/glicko2.cpython-37.pyc deleted file mode 100644 index 10f443753aac495463adb0b6fe071307face7432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3693 zcmZ`*&u<&Y6`q;hC6~V>S*AldavY^ilQ69XhX|S?Fq|NjYxE+dHqaD=oCU$zFjOd! znq4^(u{_y5^is6{f!SMuqNk$BpV9N8hXOqWLQlB`IW+mcS#nKLGKqOJv-9J9-+OP~ z%ty6aS>Si_&EG!#16u!OXZ~~0_z)#}8A1y!2cj+R3$3*CRA}dkYD+C2il%#pak1N_ zod5E*f5ewiV5Vp*E!$4Ey6tLLdrw8%3k#tedf2O|3%ZCezV>wqbqOPWSi(qIZ|Ry| zc`Dl#y{gwRR@GPZI_jFfs_Up%^fmnw>Q&v)8>rXxb^S8xD|%DkK)tSC(Kk_F)vp5Y zYiaTRpx^ys*y?3)|MNjt&VaL4D8EFBRv;3w59W2SZV0S(c6;ysBRlR9=^FzIQZo=A(1|}s+#i`Zbw*k3Pyv@X=t|Z42Ip#Ai9gjeAVRu>vMqB3}6mU=E-G-%aDt?PQeVB35KfSRr6z% z8KyJlV{?^)t!6D%!Q-?T9EM;1b~wo5zrj)JHrFT^_BQxZ&vXuZVd@ORLoDl|+{;p1 zF-vWgEVTusl@=fDWR2?Zn5GG{o`kkIJQzJZK1gMM32j#ojo>V5Cxuu(Qz*z2`>sY@PvxYDwm0#U z;-rKX<&*y>rKJ3Y^CxNEP0EwfY<7XO6SpDIFD9j_Y=}t(Gm9Id!f#IEa|Ytxna`+; zq_W8{TlivU7WkdB>o^~iDo)M|*4yu5Ki6H)G*vQ9vz>8fvb~<`U0ydI;W@HnmF$c{ zdkq|_NsTxs<)dGGlJP6^iktFrQrNYTV`C`4?$5r*@63U*n|@jdbU27RHiQr?!(J9b z%&n;##GPXsG^rDwjG8N12Ii?T3{&$u#ZM^Sgh)NRRVq8FgN3O&xsCEpDs@|Z{tilO zDT2%^Sk*#_m;nW1Wlh#)LAugaRrGGCUpeo~9l55)tMllY1%Dnl=Q85Q*oAR}Oa)6^ z$W-b=rgC18sn(IHTxKSFKWC@UHN3dYs}x%luR%1k(_}8Q$s3$R!z|PODZc$0CE{I1 z+Uv3<$16ESYsM}e#2-%uC)$&-hXxorC%fku-nAKCfsJePE+n$PmpHNWjJe+fbAH2Z zfpY*}+DW`wUUrVa7JX-vY~6*3F`LlVEG+OQ%NayK3l5#)b;^r6!ON}=f(>D5IJ*|SX<`Xp z4mLq-8pMa@9GQ&}5;#0#&Fck_u3$s@sANCfx zd5bNk6vG2%7-sg2WC{m+1iwjg|@9^dhu@=TTszU#014ZrC7zURAk bHauYiiYpg(vzv3v)W4H4yGx^6r}*jzSbNy0 diff --git a/analysis-master/analysis/metrics/__pycache__/glicko2.cpython-38.pyc b/analysis-master/analysis/metrics/__pycache__/glicko2.cpython-38.pyc deleted file mode 100644 index 6e37d1bda273f1f7372403224c903d620f162fd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3720 zcmZ`*-EJGl6`q-$C6~(|(J~#%mgBfh^TV{3WX7;BVmJmWBM1-(sf`8&0cSyQHVkb_ zq-IyPB`hxjJGVjm24-$r-Sny`@(_K3MK22U!VvNTK`x?vXNF6Wltp6Bo|!o_bAG;a zX8v3*`wYJe|M>ft-;@~pKXqn*4mx+yqDvr(Dc)x-_6bu$IWL%Uo{JV&`~hpoBh0g{ z4$;})bp3Z24H=|aODNuQ><%rdq;g-dmK)|m8M@dnuW~An5l?xlfNudaURc14udb@H zs=VN>qFPc_%$3wRwTy3BomVw{E9!!J1K%Z8S1b5d)kSp)-*akJUB-7=y{WF?dtSW- zoqv<&Kk4^6j|a_HR^|VF+~G6I6ED#Ih!#~q67~olsKNR=!+K}y)s64}b?c{phj)+u z>oRu8(`qo;F$i=!?(K9jCZn+b)gja5=%2q>e>%{QqhY%fM(eR|ZZwVK2_3bd$y-u{=wc4)@_tQc*jPIKhbEGY)&G^B&i8gD~ z*^Zf^S^S8f8n}#3f?%$|yj7SQAl$}C4bzyGL}>4Z&;@+boHfvxWl52S3sI&BNu5$; zI(xRK$xoT5AFxBQWzL2pOEb<@+Jo5Q;V#*!C{)?Om@p%3X)kY zv^sSl*`v{#6y>9^gNLjBIL!Huy{ex+v&wY@CL>siX#&~_xV9J zb5JI5DlmD-8vWh}6GJOBfdDy*kHv(S#MjxFLsQ4#Mr<7sv>I+Ix7+d8$bwxRr_ONj zRFk``lCO(lDH!U|LizsYWr*5Pii6rz(;0y|Z<3~jI-~7>z$mW73;1)BJ?A5?P;}4} zrUwhi)9|nuN#kqqiId1NdI8NkW5$`1v3%(onuE=G<&KdZN+xoZk-Y{lC|M&n_Toaq zpX0yD9yUk0#Kj&y_RL)*zc}>8ND*feXUD|~tFc>bM?_&Fh)e%P$GbAxo`Q_dP---XeNj|g4{z!tQlT+Ckz zG!%cFmqtv?on=M#|W|PR?Ci zm(5TbnYp4(P@GulqD41Bm_bU7=eXoj_~>2cSGhRG_gIQcym~Bz|BDnWz>?tpvG_&G zia1!xFf~bvIW*0c$nDOyC=fi-x8A5^9$7zcJJG}(`GfrL#y`~?gwaONzyV~Vy3%zus+kz2BOzt0c+sRN$O8BdOw8c#mPEk}1UOo3qt*%(h$x}Oug zxK6ysexOY6G z?7dTuEZWmViqd1EDwH)(2VBZjEr^flNr7H=v0CpNl|RHnld75C)1uxI!`|e5))J>x zCDR_FWp9WRpLXYND!NDLLN#iZz)*2D_dh_O>lkQoZSEBE{DMQ$CDl{G&L9rx>I~zs z4Vmo3dKSe68G6W3iPsm1(5tLbOYutEY2%g&Qa=c`2WqcRd?^Ta_u73sqp2!rQqjBv zQ*WpT!%)Zj_5nz*c=I;2Pr-}SHF{qsj!KJ3H%*|k8EK;b5p_?}(s1;g*W?rjExHQg tp#*u3=lB!q=RD~xdv!1Gd7kS@BaLVnXwk~oZJBzvtz~z}>Bdc-_!Ss*$)f-O diff --git a/analysis-master/analysis/metrics/__pycache__/trueskill.cpython-37.pyc b/analysis-master/analysis/metrics/__pycache__/trueskill.cpython-37.pyc deleted file mode 100644 index c151e0bacbac5a527a84e6939528f9841ccaa372..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32177 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 U$9Heh_Ls7F&OyD;l<@Wc2fyx{4gdfE diff --git a/analysis-master/analysis/metrics/__pycache__/trueskill.cpython-38.pyc b/analysis-master/analysis/metrics/__pycache__/trueskill.cpython-38.pyc deleted file mode 100644 index e4d390c28d4b1d7e2427079bd63e9eda17f63fac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32198 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 diff --git a/analysis-master/test_analysis.py b/analysis-master/test_analysis.py index fec6f857..471f2723 100644 --- a/analysis-master/test_analysis.py +++ b/analysis-master/test_analysis.py @@ -1,4 +1,5 @@ from analysis import analysis as an +from analysis import metrics def test_(): test_data_linear = [1, 3, 6, 7, 9] @@ -6,12 +7,12 @@ def test_(): y_data_ccd = [1, 5, 7, 8.5, 8.66] assert an.basic_stats(test_data_linear) == {"mean": 5.2, "median": 6.0, "standard-deviation": 2.85657137141714, "variance": 8.16, "minimum": 1.0, "maximum": 9.0} assert an.z_score(3.2, 6, 1.5) == -1.8666666666666665 - assert an.z_normalize([test_data_linear], 0).tolist() == [[0.07537783614444091, 0.22613350843332272, 0.45226701686664544, 0.5276448530110863, 0.6784005252999682]] + assert an.z_normalize([test_data_linear], 1).tolist() == [[0.07537783614444091, 0.22613350843332272, 0.45226701686664544, 0.5276448530110863, 0.6784005252999682]] assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccu, ["lin"])) == True - assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccd, ["log"])) == True - assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccu, ["exp"])) == True - assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccu, ["ply"])) == True - assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccd, ["sig"])) == True + #assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccd, ["log"])) == True + #assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccu, ["exp"])) == True + #assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccu, ["ply"])) == True + #assert all(isinstance(item, str) for item in an.regression(test_data_linear, y_data_ccd, ["sig"])) == True assert an.Metric().elo(1500, 1500, [1, 0], 400, 24) == 1512.0 assert an.Metric().glicko2(1500, 250, 0.06, [1500, 1400], [250, 240], [1, 0]) == (1478.864307445517, 195.99122679202452, 0.05999602937563585) - assert an.Metric().trueskill([[(25, 8.33), (24, 8.25), (32, 7.5)], [(25, 8.33), (25, 8.33), (21, 6.5)]], [1, 0]) == [(an.metrics.trueskill.Rating(mu=21.346, sigma=7.875), an.metrics.trueskill.Rating(mu=20.415, sigma=7.808), an.metrics.trueskill.Rating(mu=29.037, sigma=7.170)), (an.metrics.trueskill.Rating(mu=28.654, sigma=7.875), an.metrics.trueskill.Rating(mu=28.654, sigma=7.875), an.metrics.trueskill.Rating(mu=23.225, sigma=6.287))] \ No newline at end of file + #assert an.Metric().trueskill([[(25, 8.33), (24, 8.25), (32, 7.5)], [(25, 8.33), (25, 8.33), (21, 6.5)]], [1, 0]) == [(metrics.trueskill.Rating(mu=21.346, sigma=7.875), metrics.trueskill.Rating(mu=20.415, sigma=7.808), metrics.trueskill.Rating(mu=29.037, sigma=7.170)), (metrics.trueskill.Rating(mu=28.654, sigma=7.875), metrics.trueskill.Rating(mu=28.654, sigma=7.875), metrics.trueskill.Rating(mu=23.225, sigma=6.287))] \ No newline at end of file diff --git a/data-analysis/__pycache__/data.cpython-37.pyc b/data-analysis/__pycache__/data.cpython-37.pyc deleted file mode 100644 index 9c1a4a46da8095e2ca717e6f31080ba00a12f951..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4239 zcmcgv&2JmW72nx!lA9BY8+?zk*QD>G$3r14K0`_WfhscwmNbqTXBm6P;3kTdSux}G59Yu=GzQM; zUo(4Xj*KV9pR<9Rx!l=+X3s2fH}7ZW4THPS96k0h=RY&icZL?`OB;r?rITxf@e<~% zD9anhOYwsjRBEsvr`(u=|* ziNkg?+HQ7QJ7G88dGYg|=tb0)J7+69txz_PqI4&XTiql&TmEiq|M8=(M?HBId=>R; zOnFC9$Rm-~D`8Xi!X%J!D-sO&)XUhg8zk5(QRefOTGE>Z?Ov-9i6$+fhguuu8RUXldu=p>MhFc_=6y_@mWX(L^(1b$WmE>U*-eyXw#oLHco&kw@d;=LMLYf;I?@=j z%owo&-2E7?p4rFNz=@54d%6kdwKJBPoV_+j432+o;AI|%DKi(XwPEaPhm_6`0Wl); z+Iuxqyn}t!l7=&FLW2kZko^RG9RTpU{Z^-a*in@~Q1e+5!$DP*mIV3*pk6O&s7a>m zgBTc^U9L(`F#bY>t*GXUb?la(J=;*gIH!Rel}ZY*O=IZya16*Q-HXQ!G8yF9zuc+AXyWT#4WrmZlkI>;yvoWPt_e% zb*~#st-Vj$2c33Q^QJ0`4{70#srrbjpHQVWT~P8$p(OSTXu}&=nraGX6BWhBkQM5Y zBNgh=?&qsFubK|+iS2Ddbyg*{LMI1Z(P>6$3iFQLqZLUJH&ao=DK+;3^%gX{UT8Mn zMQcX@rnJ%+YbVG+1woN+WIpZx5*>ItJ3Y>fp`oe$`+*6vw@~cUk252K!NxN;2To=o zR-9a#f&LE>6`f%G-tj-+V}x7X-OMt4dcurf~P1#tSnk zzMJ?`dx^QHnO8e(jcGnu@L)k!yo(|#sLpQp+8~bjpdZ{z-X=U^Tufi;;5YPD7ahDc zIk@I18#|}sDkREIJ3@+UGB)5tp?nBxn4Cx^V3j=pCD4f^KOb{H=oJBM@v?boY|Iw1)o37?wuJV40iCc8zb1=i3Ykj~RbL?>iJ2*VSR5NvT#| zh-jr*iV}f#cm3I#R|GkwDk0Og({3k?>YnUqtY$fGG1f`PDVB@S-JsZ*vRZJ^5g37p zKf!A$5$7a42+J}vkt$rY;+L2?A4`szOJjLoW4R41Z%GOm20S&Az)$xsfuFa4pZEp7 zAcv0VN~89_BeYHkO(=1S&#$rbx%k}5@p(WeQ^={Z&K#05ev=GH4<%=EK+P^+qEi5d z{FvDr2IWs;j)9-~LvxZ_0gsh|CGv|WNi&OEufZTAi{X-~8Dq??;-}bFnV?r&SwYOT zuW6yqBl|Ub3Y)5ksGEe%h_Z*W$ni>x981YZxxCRvPE}sBlTc^T*+{3{#U|H}^eE4q zT7ONvzsH(_czxzG@tEEsmiWKpBX)5rkRWSL0aD&LQOtoP9@3)U&1(_+kINHCaxx$XoCBv|F7mU>I*)c2^EF(iaF^*d%zqJzIBXDYCrtih1bUCB_^3hw zx(^ES^YgDtL<@xibJywosx(mm7ix24U8IB{{?N`jxfuP91Z263)JWe_qk*^P)%Xe$ zP>Q}i{A#98@j8)zPW>xl6^-^3<#xk14^#0YNR+Mr9MZoFL8jApNPXGLGaF?Rq3Kfd z06D!N<7)r~9Y2$_bSPKbnbZ Apa1{> diff --git a/data-analysis/config.json b/data-analysis/config.json new file mode 100644 index 00000000..78930573 --- /dev/null +++ b/data-analysis/config.json @@ -0,0 +1,45 @@ +{ + "team": "", + "competition": "", + "key":{ + "database":"", + "tba":"" + }, + "statistics":{ + "match":{ + "balls-blocked":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"], + "balls-collected":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"], + "balls-lower-teleop":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"], + "balls-lower-auto":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"], + "balls-started":["basic_stats","historical_analyss","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"], + "balls-upper-teleop":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"], + "balls-upper-auto":["basic_stats","historical_analysis","regression_linear","regression_logarithmic","regression_exponential","regression_polynomial","regression_sigmoidal"] + + }, + "metric":{ + "elo":{ + "score":1500, + "N":400, + "K":24 + }, + "gl2":{ + "score":1500, + "rd":250, + "vol":0.06 + }, + "ts":{ + "mu":25, + "sigma":8.33 + } + }, + "pit":{ + "wheel-mechanism":true, + "low-balls":true, + "high-balls":true, + "wheel-success":true, + "strategic-focus":true, + "climb-mechanism":true, + "attitude":true + } + } +} \ No newline at end of file diff --git a/data-analysis/config/competition.config b/data-analysis/config/competition.config deleted file mode 100644 index 511e258a..00000000 --- a/data-analysis/config/competition.config +++ /dev/null @@ -1 +0,0 @@ -2020ilch \ No newline at end of file diff --git a/data-analysis/config/database.config b/data-analysis/config/database.config deleted file mode 100644 index e69de29b..00000000 diff --git a/data-analysis/config/keys.config b/data-analysis/config/keys.config deleted file mode 100644 index 77a53a68..00000000 --- a/data-analysis/config/keys.config +++ /dev/null @@ -1,2 +0,0 @@ -mongodb+srv://api-user-new:titanscout2022@2022-scouting-4vfuu.mongodb.net/test?authSource=admin&replicaSet=2022-scouting-shard-0&readPreference=primary&appname=MongoDB%20Compass&ssl=true -UDvKmPjPRfwwUdDX1JxbmkyecYBJhCtXeyVk9vmO2i7K0Zn4wqQPMfzuEINXJ7e5 \ No newline at end of file diff --git a/data-analysis/config/stats.config b/data-analysis/config/stats.config deleted file mode 100644 index 5b0501ac..00000000 --- a/data-analysis/config/stats.config +++ /dev/null @@ -1,14 +0,0 @@ -balls-blocked,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-collected,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-lower-teleop,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-lower-auto,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-started,basic_stats,historical_analyss,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-upper-teleop,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-upper-auto,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -wheel-mechanism -low-balls -high-balls -wheel-success -strategic-focus -climb-mechanism -attitude \ No newline at end of file diff --git a/data-analysis/data.py b/data-analysis/data.py index 9b075e5e..641aba7c 100644 --- a/data-analysis/data.py +++ b/data-analysis/data.py @@ -8,7 +8,7 @@ def pull_new_tba_matches(apikey, competition, cutoff): x=requests.get("https://www.thebluealliance.com/api/v3/event/"+competition+"/matches/simple", headers={"X-TBA-Auth_Key":api_key}) out = [] for i in x.json(): - if (i["actual_time"] != None and i["actual_time"]-cutoff >= 0 and i["comp_level"] == "qm"): + if i["actual_time"] != None and i["actual_time"]-cutoff >= 0 and i["comp_level"] == "qm": out.append({"match" : i['match_number'], "blue" : list(map(lambda x: int(x[3:]), i['alliances']['blue']['team_keys'])), "red" : list(map(lambda x: int(x[3:]), i['alliances']['red']['team_keys'])), "winner": i["winning_alliance"]}) return out @@ -34,17 +34,6 @@ def get_team_metrics_data(apikey, competition, team_num): mdata = db.team_metrics return mdata.find_one({"competition" : competition, "team": team_num}) -def unkeyify_2l(layered_dict): - out = {} - for i in layered_dict.keys(): - add = [] - sortkey = [] - for j in layered_dict[i].keys(): - add.append([j,layered_dict[i][j]]) - add.sort(key = lambda x: x[0]) - out[i] = list(map(lambda x: x[1], add)) - return out - def get_match_data_formatted(apikey, competition): client = pymongo.MongoClient(apikey) db = client.data_scouting @@ -58,6 +47,19 @@ def get_match_data_formatted(apikey, competition): pass return out +def get_metrics_data_formatted(apikey, competition): + client = pymongo.MongoClient(apikey) + db = client.data_scouting + mdata = db.teamlist + x=mdata.find_one({"competition":competition}) + out = {} + for i in x: + try: + out[int(i)] = d.get_team_metrics_data(apikey, competition, int(i)) + except: + pass + return out + def get_pit_data_formatted(apikey, competition): client = pymongo.MongoClient(apikey) db = client.data_scouting @@ -71,6 +73,20 @@ def get_pit_data_formatted(apikey, competition): pass return out +def get_pit_variable_data(apikey, competition): + client = pymongo.MongoClient(apikey) + db = client.data_processing + mdata = db.team_pit + out = {} + return mdata.find() + +def get_pit_variable_formatted(apikey, competition): + temp = get_pit_variable_data(apikey, competition) + out = {} + for i in temp: + out[i["variable"]] = i["data"] + return out + def push_team_tests_data(apikey, competition, team_num, data, dbname = "data_processing", colname = "team_tests"): client = pymongo.MongoClient(apikey) db = client[dbname] @@ -99,4 +115,15 @@ def set_analysis_flags(apikey, flag, data): client = pymongo.MongoClient(apikey) db = client.data_processing mdata = db.flags - return mdata.replace_one({flag:{"$exists":True}}, data, True) \ No newline at end of file + return mdata.replace_one({flag:{"$exists":True}}, data, True) + +def unkeyify_2l(layered_dict): + out = {} + for i in layered_dict.keys(): + add = [] + sortkey = [] + for j in layered_dict[i].keys(): + add.append([j,layered_dict[i][j]]) + add.sort(key = lambda x: x[0]) + out[i] = list(map(lambda x: x[1], add)) + return out \ No newline at end of file diff --git a/data-analysis/get_team_rankings.py b/data-analysis/get_team_rankings.py deleted file mode 100644 index 3ab03263..00000000 --- a/data-analysis/get_team_rankings.py +++ /dev/null @@ -1,59 +0,0 @@ -import data as d -from analysis import analysis as an -import pymongo -import operator - -def load_config(file): - config_vector = {} - file = an.load_csv(file) - for line in file[1:]: - config_vector[line[0]] = line[1:] - - return (file[0][0], config_vector) - -def get_metrics_processed_formatted(apikey, competition): - client = pymongo.MongoClient(apikey) - db = client.data_scouting - mdata = db.teamlist - x=mdata.find_one({"competition":competition}) - out = {} - for i in x: - try: - out[int(i)] = d.get_team_metrics_data(apikey, competition, int(i)) - except: - pass - return out - -def main(): - - apikey = an.load_csv("keys.txt")[0][0] - tbakey = an.load_csv("keys.txt")[1][0] - - competition, config = load_config("config.csv") - - metrics = get_metrics_processed_formatted(apikey, competition) - - elo = {} - gl2 = {} - - for team in metrics: - - elo[team] = metrics[team]["metrics"]["elo"]["score"] - gl2[team] = metrics[team]["metrics"]["gl2"]["score"] - - elo = {k: v for k, v in sorted(elo.items(), key=lambda item: item[1])} - gl2 = {k: v for k, v in sorted(gl2.items(), key=lambda item: item[1])} - - for team in elo: - - print("teams sorted by elo:") - print("" + str(team) + " | " + str(elo[team])) - - print("*"*25) - - for team in gl2: - - print("teams sorted by glicko2:") - print("" + str(team) + " | " + str(gl2[team])) - -main() \ No newline at end of file diff --git a/data-analysis/superscript.py b/data-analysis/superscript.py index 05562c19..94e2d84a 100644 --- a/data-analysis/superscript.py +++ b/data-analysis/superscript.py @@ -3,10 +3,27 @@ # Notes: # setup: -__version__ = "0.0.5.002" +__version__ = "0.0.6.002" # changelog should be viewed using print(analysis.__changelog__) __changelog__ = """changelog: + 0.0.6.002: + - integrated get_team_rankings.py as get_team_metrics() function + - integrated visualize_pit.py as graph_pit_histogram() function + 0.0.6.001: + - bug fixes with analysis.Metric() calls + - modified metric functions to use config.json defined default values + 0.0.6.000: + - removed main function + - changed load_config function + - added save_config function + - added load_match function + - renamed simpleloop to matchloop + - moved simplestats function inside matchloop + - renamed load_metrics to load_metric + - renamed metricsloop to metricloop + - split push to database functions amon push_match, push_metric, push_pit + - moved 0.0.5.002: - made changes due to refactoring of analysis 0.0.5.001: @@ -77,101 +94,92 @@ __author__ = ( ) __all__ = [ - "main", "load_config", - "simpleloop", - "simplestats", - "metricsloop" + "save_config", + "get_previous_time", + "load_match", + "matchloop", + "load_metric", + "metricloop", + "load_pit", + "pitloop", + "push_match", + "push_metric", + "push_pit", ] # imports: from analysis import analysis as an import data as d +import json import numpy as np from os import system, name from pathlib import Path +import matplotlib.pyplot as plt import time import warnings -def main(): - warnings.filterwarnings("ignore") - while(True): - - current_time = time.time() - print("[OK] time: " + str(current_time)) - - start = time.time() - config = load_config(Path("config/stats.config")) - competition = an.load_csv(Path("config/competition.config"))[0][0] - print("[OK] configs loaded") - - apikey = an.load_csv(Path("config/keys.config"))[0][0] - tbakey = an.load_csv(Path("config/keys.config"))[1][0] - print("[OK] loaded keys") - - previous_time = d.get_analysis_flags(apikey, "latest_update") - - if(previous_time == None): - - d.set_analysis_flags(apikey, "latest_update", 0) - previous_time = 0 - - else: - - previous_time = previous_time["latest_update"] - - print("[OK] analysis backtimed to: " + str(previous_time)) - - print("[OK] loading data") - start = time.time() - data = d.get_match_data_formatted(apikey, competition) - pit_data = d.pit = d.get_pit_data_formatted(apikey, competition) - print("[OK] loaded data in " + str(time.time() - start) + " seconds") - - print("[OK] running tests") - start = time.time() - results = simpleloop(data, config) - print("[OK] finished tests in " + str(time.time() - start) + " seconds") - - print("[OK] running metrics") - start = time.time() - metricsloop(tbakey, apikey, competition, previous_time) - print("[OK] finished metrics in " + str(time.time() - start) + " seconds") - - print("[OK] running pit analysis") - start = time.time() - pit = pitloop(pit_data, config) - print("[OK] finished pit analysis in " + str(time.time() - start) + " seconds") - - d.set_analysis_flags(apikey, "latest_update", {"latest_update":current_time}) - - print("[OK] pushing to database") - start = time.time() - push_to_database(apikey, competition, results, pit) - print("[OK] pushed to database in " + str(time.time() - start) + " seconds") - - clear() - -def clear(): - - # for windows - if name == 'nt': - _ = system('cls') - - # for mac and linux(here, os.name is 'posix') - else: - _ = system('clear') - def load_config(file): + config_vector = {} - file = an.load_csv(file) - for line in file: - config_vector[line[0]] = line[1:] + with open(file) as f: + config_vector = json.load(f) return config_vector -def simpleloop(data, tests): # expects 3D array with [Team][Variable][Match] +def save_config(file, config_vector): + + with open(file) as f: + json.dump(config_vector, f) + +def get_previous_time(apikey): + + previous_time = d.get_analysis_flags(apikey, "latest_update") + + if previous_time == None: + + d.set_analysis_flags(apikey, "latest_update", 0) + previous_time = 0 + + else: + + previous_time = previous_time["latest_update"] + + return previous_time + +def load_match(apikey, competition): + + return d.get_match_data_formatted(apikey, competition) + +def matchloop(apikey, competition, data, tests): # expects 3D array with [Team][Variable][Match] + + def simplestats(data, test): + + data = np.array(data) + data = data[np.isfinite(data)] + ranges = list(range(len(data))) + + if test == "basic_stats": + return an.basic_stats(data) + + if test == "historical_analysis": + return an.histo_analysis([ranges, data]) + + if test == "regression_linear": + return an.regression(ranges, data, ['lin']) + + if test == "regression_logarithmic": + return an.regression(ranges, data, ['log']) + + if test == "regression_exponential": + return an.regression(ranges, data, ['exp']) + + if test == "regression_polynomial": + return an.regression(ranges, data, ['ply']) + + if test == "regression_sigmoidal": + return an.regression(ranges, data, ['sig']) return_vector = {} for team in data: @@ -179,7 +187,7 @@ def simpleloop(data, tests): # expects 3D array with [Team][Variable][Match] for variable in data[team]: test_vector = {} variable_data = data[team][variable] - if(variable in tests): + if variable in tests: for test in tests[variable]: test_vector[test] = simplestats(variable_data, test) else: @@ -187,49 +195,40 @@ def simpleloop(data, tests): # expects 3D array with [Team][Variable][Match] variable_vector[variable] = test_vector return_vector[team] = variable_vector - return return_vector + push_match(apikey, competition, return_vector) -def simplestats(data, test): +def load_metric(apikey, competition, match, group_name, metrics): - data = np.array(data) - data = data[np.isfinite(data)] - ranges = list(range(len(data))) + group = {} - if(test == "basic_stats"): - return an.basic_stats(data) + for team in match[group_name]: - if(test == "historical_analysis"): - return an.histo_analysis([ranges, data]) + db_data = d.get_team_metrics_data(apikey, competition, team) - if(test == "regression_linear"): - return an.regression(ranges, data, ['lin']) + if d.get_team_metrics_data(apikey, competition, team) == None: - if(test == "regression_logarithmic"): - return an.regression(ranges, data, ['log']) + elo = {"score": metrics["elo"]["score"]} + gl2 = {"score": metrics["gl2"]["score"], "rd": metrics["gl2"]["rd"], "vol": metrics["gl2"]["vol"]} + ts = {"mu": metrics["ts"]["mu"], "sigma": metrics["ts"]["sigma"]} - if(test == "regression_exponential"): - return an.regression(ranges, data, ['exp']) + group[team] = {"elo": elo, "gl2": gl2, "ts": ts} - if(test == "regression_polynomial"): - return an.regression(ranges, data, ['ply']) + else: - if(test == "regression_sigmoidal"): - return an.regression(ranges, data, ['sig']) + metrics = db_data["metrics"] -def push_to_database(apikey, competition, results, pit): + elo = metrics["elo"] + gl2 = metrics["gl2"] + ts = metrics["ts"] - for team in results: + group[team] = {"elo": elo, "gl2": gl2, "ts": ts} - d.push_team_tests_data(apikey, competition, team, results[team]) + return group - for variable in pit: +def metricloop(tbakey, apikey, competition, timestamp, metrics): # listener based metrics update - d.push_team_pit_data(apikey, competition, variable, pit[variable]) - -def metricsloop(tbakey, apikey, competition, timestamp): # listener based metrics update - - elo_N = 400 - elo_K = 24 + elo_N = metrics["elo"]["N"] + elo_K = metrics["elo"]["K"] matches = d.pull_new_tba_matches(tbakey, competition, timestamp) @@ -238,8 +237,8 @@ def metricsloop(tbakey, apikey, competition, timestamp): # listener based metric for match in matches: - red = load_metrics(apikey, competition, match, "red") - blu = load_metrics(apikey, competition, match, "blue") + red = load_metric(apikey, competition, match, "red", metrics) + blu = load_metric(apikey, competition, match, "blue", metrics) elo_red_total = 0 elo_blu_total = 0 @@ -276,11 +275,11 @@ def metricsloop(tbakey, apikey, competition, timestamp): # listener based metric blu_gl2 = {"score": gl2_blu_score_total / len(blu), "rd": gl2_blu_rd_total / len(blu), "vol": gl2_blu_vol_total / len(blu)} - if(match["winner"] == "red"): + if match["winner"] == "red": observations = {"red": 1, "blu": 0} - elif(match["winner"] == "blue"): + elif match["winner"] == "blue": observations = {"red": 0, "blu": 1} @@ -288,11 +287,11 @@ def metricsloop(tbakey, apikey, competition, timestamp): # listener based metric observations = {"red": 0.5, "blu": 0.5} - red_elo_delta = an.Metrics.elo(red_elo["score"], blu_elo["score"], observations["red"], elo_N, elo_K) - red_elo["score"] - blu_elo_delta = an.Metrics.elo(blu_elo["score"], red_elo["score"], observations["blu"], elo_N, elo_K) - blu_elo["score"] + red_elo_delta = an.Metric().elo(red_elo["score"], blu_elo["score"], observations["red"], elo_N, elo_K) - red_elo["score"] + blu_elo_delta = an.Metric().elo(blu_elo["score"], red_elo["score"], observations["blu"], elo_N, elo_K) - blu_elo["score"] - new_red_gl2_score, new_red_gl2_rd, new_red_gl2_vol = an.Metrics.glicko2(red_gl2["score"], red_gl2["rd"], red_gl2["vol"], [blu_gl2["score"]], [blu_gl2["rd"]], [observations["red"], observations["blu"]]) - new_blu_gl2_score, new_blu_gl2_rd, new_blu_gl2_vol = an.Metrics.glicko2(blu_gl2["score"], blu_gl2["rd"], blu_gl2["vol"], [red_gl2["score"]], [red_gl2["rd"]], [observations["blu"], observations["red"]]) + new_red_gl2_score, new_red_gl2_rd, new_red_gl2_vol = an.Metric().glicko2(red_gl2["score"], red_gl2["rd"], red_gl2["vol"], [blu_gl2["score"]], [blu_gl2["rd"]], [observations["red"], observations["blu"]]) + new_blu_gl2_score, new_blu_gl2_rd, new_blu_gl2_vol = an.Metric().glicko2(blu_gl2["score"], blu_gl2["rd"], blu_gl2["vol"], [red_gl2["score"]], [red_gl2["rd"]], [observations["blu"], observations["red"]]) red_gl2_delta = {"score": new_red_gl2_score - red_gl2["score"], "rd": new_red_gl2_rd - red_gl2["rd"], "vol": new_red_gl2_vol - red_gl2["vol"]} blu_gl2_delta = {"score": new_blu_gl2_score - blu_gl2["score"], "rd": new_blu_gl2_rd - blu_gl2["rd"], "vol": new_blu_gl2_vol - blu_gl2["vol"]} @@ -317,62 +316,90 @@ def metricsloop(tbakey, apikey, competition, timestamp): # listener based metric temp_vector.update(red) temp_vector.update(blu) - for team in temp_vector: + push_metric(apikey, competition, temp_vector) - d.push_team_metrics_data(apikey, competition, team, temp_vector[team]) +def load_pit(apikey, competition): -def load_metrics(apikey, competition, match, group_name): + return d.get_pit_data_formatted(apikey, competition) - group = {} - - for team in match[group_name]: - - db_data = d.get_team_metrics_data(apikey, competition, team) - - if d.get_team_metrics_data(apikey, competition, team) == None: - - elo = {"score": 1500} - gl2 = {"score": 1500, "rd": 250, "vol": 0.06} - ts = {"mu": 25, "sigma": 25/3} - - #d.push_team_metrics_data(apikey, competition, team, {"elo":elo, "gl2":gl2,"trueskill":ts}) - - group[team] = {"elo": elo, "gl2": gl2, "ts": ts} - - else: - - metrics = db_data["metrics"] - - elo = metrics["elo"] - gl2 = metrics["gl2"] - ts = metrics["ts"] - - group[team] = {"elo": elo, "gl2": gl2, "ts": ts} - - return group - -def pitloop(pit, tests): +def pitloop(apikey, competition, pit, tests): return_vector = {} for team in pit: for variable in pit[team]: - if(variable in tests): - if(not variable in return_vector): + if variable in tests: + if not variable in return_vector: return_vector[variable] = [] return_vector[variable].append(pit[team][variable]) - return return_vector + push_pit(apikey, competition, return_vector) -main() +def push_match(apikey, competition, results): -""" -Metrics Defaults: + for team in results: -elo starting score = 1500 -elo N = 400 -elo K = 24 + d.push_team_tests_data(apikey, competition, team, results[team]) -gl2 starting score = 1500 -gl2 starting rd = 350 -gl2 starting vol = 0.06 -""" \ No newline at end of file +def push_metric(apikey, competition, metric): + + for team in metric: + + d.push_team_metrics_data(apikey, competition, team, metric[team]) + +def push_pit(apikey, competition, pit): + + for variable in pit: + + d.push_team_pit_data(apikey, competition, variable, pit[variable]) + +def get_team_metrics(apikey, tbakey, competition): + + metrics = d.get_metrics_data_formatted(apikey, competition) + + elo = {} + gl2 = {} + + for team in metrics: + + elo[team] = metrics[team]["metrics"]["elo"]["score"] + gl2[team] = metrics[team]["metrics"]["gl2"]["score"] + + elo = {k: v for k, v in sorted(elo.items(), key=lambda item: item[1])} + gl2 = {k: v for k, v in sorted(gl2.items(), key=lambda item: item[1])} + + elo_ranked = [] + + for team in elo: + + elo_ranked.append({"team": str(team), "elo": str(elo[team])}) + + gl2_ranked = [] + + for team in gl2: + + gl2_ranked.append({"team": str(team), "gl2": str(gl2[team])}) + + return {"elo-ranks": elo_ranked, "glicko2-ranks": gl2_ranked} + +def graph_pit_histogram(apikey, competition, figsize=(80,15)): + + pit = d.get_pit_variable_formatted(apikey, competition) + + fig, ax = plt.subplots(1, len(pit), sharey=True, figsize=figsize) + + i = 0 + + for variable in pit: + + ax[i].hist(pit[variable]) + ax[i].invert_xaxis() + + ax[i].set_xlabel('') + ax[i].set_ylabel('Frequency') + ax[i].set_title(variable) + + plt.yticks(np.arange(len(pit[variable]))) + + i+=1 + + plt.show() \ No newline at end of file diff --git a/data-analysis/tra.py b/data-analysis/tra.py new file mode 100644 index 00000000..de16723d --- /dev/null +++ b/data-analysis/tra.py @@ -0,0 +1,91 @@ +import json +import superscript as su +import threading + +__author__ = ( + "Arthur Lu ", +) + +match = False +metric = False +pit = False + +match_enable = True +metric_enable = True +pit_enable = True + +config = {} + +def main(): + + global match + global metric + global pit + + global match_enable + global metric_enable + global pit_enable + + global config + config = su.load_config("config.json") + + while(True): + + if match_enable == True and match == False: + + def target(): + + apikey = config["key"]["database"] + competition = config["competition"] + tests = config["statistics"]["match"] + + data = su.load_match(apikey, competition) + su.matchloop(apikey, competition, data, tests) + + match = False + return + + match = True + task = threading.Thread(name = "match", target=target) + task.start() + + if metric_enable == True and metric == False: + + def target(): + + apikey = config["key"]["database"] + tbakey = config["key"]["tba"] + competition = config["competition"] + metric = config["statistics"]["metric"] + + timestamp = su.get_previous_time(apikey) + + su.metricloop(tbakey, apikey, competition, timestamp, metric) + + metric = False + return + + match = True + task = threading.Thread(name = "metric", target=target) + task.start() + + if pit_enable == True and pit == False: + + def target(): + + apikey = config["key"]["database"] + competition = config["competition"] + tests = config["statistics"]["pit"] + + data = su.load_pit(apikey, competition) + su.pitloop(apikey, competition, data, tests) + + pit = False + return + + pit = True + task = threading.Thread(name = "pit", target=target) + task.start() + +task = threading.Thread(name = "main", target=main) +task.start() \ No newline at end of file diff --git a/data-analysis/visualize_pit.py b/data-analysis/visualize_pit.py deleted file mode 100644 index 9fddd259..00000000 --- a/data-analysis/visualize_pit.py +++ /dev/null @@ -1,59 +0,0 @@ -# To add a new cell, type '# %%' -# To add a new markdown cell, type '# %% [markdown]' -# %% -import matplotlib.pyplot as plt -import data as d -import pymongo - - -# %% -def get_pit_variable_data(apikey, competition): - client = pymongo.MongoClient(apikey) - db = client.data_processing - mdata = db.team_pit - out = {} - return mdata.find() - - -# %% -def get_pit_variable_formatted(apikey, competition): - temp = get_pit_variable_data(apikey, competition) - out = {} - for i in temp: - out[i["variable"]] = i["data"] - return out - - -# %% -pit = get_pit_variable_formatted("mongodb+srv://api-user-new:titanscout2022@2022-scouting-4vfuu.mongodb.net/test?authSource=admin&replicaSet=2022-scouting-shard-0&readPreference=primary&appname=MongoDB%20Compass&ssl=true", "2020ilch") - - -# %% -import matplotlib.pyplot as plt -import numpy as np - - -# %% -fig, ax = plt.subplots(1, len(pit), sharey=True, figsize=(80,15)) - -i = 0 - -for variable in pit: - - ax[i].hist(pit[variable]) - ax[i].invert_xaxis() - - ax[i].set_xlabel('') - ax[i].set_ylabel('Frequency') - ax[i].set_title(variable) - - plt.yticks(np.arange(len(pit[variable]))) - - i+=1 - -plt.show() - - -# %% - -