├── .gitignore ├── LICENSE ├── README.md ├── docs ├── pass-mixture.png └── soccermix_ecml2020.pdf ├── mixture.py ├── notebooks ├── 1-load-and-convert-statsbomb-data.ipynb ├── 2-create-mixture-models.ipynb ├── paper-casestudy-city-liverpool.ipynb ├── paper-experiment1-deanonymizing-players.ipynb ├── paper-experiment2-player-illustration.ipynb ├── paper-experiment3-team-illustration.ipynb └── paper-experiment4-defensive-style.ipynb └── vis.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 KU Leuven Machine Learning Research Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # soccermix 2 | SoccerMix is a soft clustering technique based on mixture models that decomposes event stream data into a number of *prototypical* actions of a specific type, location, and direction. 3 | 4 | Here is an example of 71 different prototypical passes discovered by SoccerMix. 5 | ![](docs/pass-mixture.png) 6 | 7 | 8 | 9 | 10 | Copyright 2020 Tom Decroos, Maaike Van Roy, Jesse Davis 11 | -------------------------------------------------------------------------------- /docs/pass-mixture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ML-KULeuven/soccermix/9f23c98b8e20b5cfeb569a43f8a2b110bf50dfd2/docs/pass-mixture.png -------------------------------------------------------------------------------- /docs/soccermix_ecml2020.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ML-KULeuven/soccermix/9f23c98b8e20b5cfeb569a43f8a2b110bf50dfd2/docs/soccermix_ecml2020.pdf -------------------------------------------------------------------------------- /mixture.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import tqdm 4 | 5 | import scipy.stats as stats 6 | import sklearn.cluster as cluster 7 | 8 | import cvxpy as cp 9 | 10 | 11 | class MultiGauss: 12 | 13 | def init_responsibilities(X, weights,n_components): 14 | # initialize weights with KMeans 15 | n_samples, _ = X.shape 16 | labels = cluster.KMeans(n_clusters=n_components, n_init=10).fit_predict( 17 | X, sample_weight=weights 18 | ) 19 | resp = np.zeros((n_samples, n_components)) 20 | resp[np.arange(n_samples), labels] = 1 21 | return resp 22 | 23 | def fit(self, X, w): 24 | self.mean = np.average(X, weights=w, axis=0) 25 | self.cov = np.nan_to_num(np.cov(X.T, aweights=w), 0) 26 | np.fill_diagonal(self.cov, self.cov.diagonal() + 1e-6) 27 | 28 | self._n_parameters = len(self.mean) + len(self.cov.flatten()) 29 | return self 30 | 31 | def pdf(self, X): 32 | return np.nan_to_num( 33 | stats.multivariate_normal.pdf( 34 | X, mean=self.mean, cov=self.cov 35 | ) 36 | ) 37 | 38 | class Gauss: 39 | 40 | def fit(self, X, w): 41 | self.mean = np.average(X, weights=w, axis=0) 42 | self.std = np.sqrt(np.cov(X, aweights=w)) 43 | self.std += 1e-6 44 | 45 | self._n_parameters = 2 46 | return self 47 | 48 | def pdf(self, X): 49 | return np.nan_to_num( 50 | stats.norm.pdf( 51 | X, loc=self.mean, scale=self.std 52 | ) 53 | ) 54 | 55 | class VonMises: 56 | 57 | def init_responsibilities(alpha, weights,n_components): 58 | # initialize weights with KMeans 59 | n_samples, _ = alpha.shape 60 | X = np.concatenate([np.sin(alpha),np.cos(alpha)],axis=1) 61 | labels = cluster.KMeans(n_clusters=n_components, n_init=10).fit_predict( 62 | X, sample_weight=weights 63 | ) 64 | resp = np.zeros((n_samples, n_components)) 65 | resp[np.arange(n_samples), labels] = 1 66 | return resp 67 | 68 | def fit(self, alpha, w): 69 | sin = np.average(np.sin(alpha), weights=w, axis=0) 70 | cos = np.average(np.cos(alpha), weights=w, axis=0) 71 | 72 | self.loc = np.arctan2(sin, cos) 73 | self.R = np.sqrt(sin ** 2 + cos ** 2) # mean resultant length 74 | # self.R = min(self.R,0.99) 75 | 76 | maxR = np.empty_like(self.R) 77 | maxR[0] = 0.99 78 | self.R = min(self.R, maxR) 79 | 80 | self.kappa = ( 81 | self.R * (2 - self.R ** 2) / (1 - self.R ** 2) 82 | ) # approximation for kappa 83 | 84 | self._n_parameters = 2 85 | return self 86 | 87 | def pdf(self, alpha): 88 | return np.nan_to_num( 89 | stats.vonmises.pdf(alpha, kappa=self.kappa, loc=self.loc).flatten() 90 | ) 91 | 92 | 93 | class CategoricalModel: 94 | 95 | def __init__(self, tol=1e-6): 96 | self.tol = tol 97 | 98 | def fit(self, X, weights=None): 99 | if weights: 100 | X = X[weights > self.tol] 101 | self.categories = set(X) 102 | return self 103 | 104 | def predict_proba(self, X, weights=None): 105 | p = pd.DataFrame() 106 | if weights is None: 107 | weights = np.zeros(len(X)) + 1 108 | for c in self.categories: 109 | p[str(c)] = ((X == c) & (weights > self.tol)).apply(float) 110 | return p 111 | 112 | 113 | class MixtureModel: 114 | def __init__(self, n_components, distribution=MultiGauss, max_iter=100, tol=1e-6): 115 | self.n_components = n_components 116 | self.distribution = distribution 117 | self.max_iter = max_iter 118 | self.tol = tol 119 | 120 | def fit(self, X, weights=None, verbose=False): 121 | 122 | # handle sparsity 123 | if weights is None: 124 | weights = np.zeros(len(X)) + 1 125 | pos_weights_idx = weights > self.tol 126 | X = X[pos_weights_idx] 127 | weights = weights[pos_weights_idx] 128 | 129 | self.weight_total = weights.sum() 130 | self.loglikelihood = -np.inf 131 | self.submodels = list(self.distribution() for _i in range(self.n_components)) 132 | 133 | if len(X) < self.n_components: 134 | return None 135 | 136 | responsibilities = self.distribution.init_responsibilities(X, weights,self.n_components) 137 | 138 | # learn models on initial weights 139 | self.priors = responsibilities.sum(axis=0) / responsibilities.sum() 140 | # invalid model if less clusters found than given components 141 | if any(self.priors < self.tol): 142 | return None 143 | 144 | for i in range(self.n_components): 145 | self.submodels[i].fit(X, weights * responsibilities[:, i]) 146 | 147 | iterations = ( 148 | range(self.max_iter) if not verbose else tqdm.tqdm(range(self.max_iter)) 149 | ) 150 | 151 | for self._n_iter in iterations: 152 | # Expectation 153 | for i in range(self.n_components): 154 | responsibilities[:, i] = self.priors[i] * self.submodels[i].pdf(X) 155 | 156 | # enough improvement or not? 157 | new_loglikelihood = (weights * np.log(responsibilities.sum(axis=1))).sum() 158 | 159 | if new_loglikelihood > self.loglikelihood + self.tol: 160 | self.loglikelihood = new_loglikelihood 161 | # self.responsibilities = responsibilities 162 | # self.weights = weights 163 | else: 164 | break 165 | 166 | # normalize responsibilities such that each data point occurs with P=1 167 | responsibilities /= responsibilities.sum(axis=1)[:, np.newaxis] 168 | 169 | # Maximalization 170 | self.priors = responsibilities.sum(axis=0) / responsibilities.sum() 171 | for i in range(self.n_components): 172 | self.submodels[i].fit(X, weights * responsibilities[:, i]) 173 | 174 | if np.isinf(self.loglikelihood): 175 | return None 176 | 177 | return self 178 | 179 | def predict_proba(self, X, weights=None): 180 | p = np.zeros((len(X), self.n_components)) 181 | 182 | # handle sparsity 183 | if weights is None: 184 | weights = np.zeros(len(X)) + 1 185 | pos_weights_idx = weights > self.tol 186 | X = X[pos_weights_idx] 187 | weights = weights[pos_weights_idx] 188 | 189 | pdfs = np.vstack([m.pdf(X) for m in self.submodels]).T 190 | resp = self.priors * pdfs 191 | probs = resp / resp.sum(axis=1)[:, np.newaxis] 192 | 193 | p[pos_weights_idx, :] = (weights * probs.T).T 194 | return p 195 | 196 | def params(self): 197 | return list(m.__dict__ for m in self.submodels) 198 | 199 | def _n_parameters(self): 200 | return ( 201 | sum(m._n_parameters for m in self.submodels) 202 | - self.submodels[0]._n_parameters 203 | ) 204 | 205 | 206 | def ilp_select_models_max(models, max_components, verbose=False): 207 | x = cp.Variable(len(models), boolean=True) 208 | c = np.array(list(m.loglikelihood for m in models)) 209 | n_components = np.array(list(m.n_components for m in models)) 210 | 211 | objective = cp.Maximize(cp.sum(c * x)) 212 | constraints = [] 213 | constraints += [n_components * x <= max_components] 214 | for name in set(m.name for m in models): 215 | name_idx = np.array(list(int(m.name == name) for m in models)) 216 | constraints += [name_idx * x == 1] 217 | 218 | prob = cp.Problem(objective, constraints) 219 | prob.solve(verbose=verbose) 220 | idx, = np.where(x.value > 0.3) 221 | return list(models[i] for i in idx) 222 | 223 | 224 | def ilp_select_models_bic_triangle(models, verbose=False): 225 | x = cp.Variable(len(models), boolean=True) 226 | c = np.array(list(m.loglikelihood for m in models)) 227 | n_parameters = np.array(list(m.n_components for m in models)) 228 | dataweights = {} 229 | for m in models: 230 | if m.name not in dataweights: 231 | dataweights[m.name] = m.weight_total 232 | n_data = sum(dataweights.values()) 233 | 234 | n = cp.sum(n_parameters * x) 235 | para = n + (cp.square(n) + n) / 2 236 | objective = cp.Minimize(np.log(n_data) * para - 2 * cp.sum(c * x)) 237 | 238 | constraints = [] 239 | for name in set(m.name for m in models): 240 | name_idx = np.array(list(int(m.name == name) for m in models)) 241 | constraints += [name_idx * x == 1] 242 | 243 | prob = cp.Problem(objective, constraints) 244 | prob.solve(verbose=verbose) 245 | idx, = np.where(x.value > 0.3) 246 | return list(models[i] for i in idx) 247 | 248 | 249 | def ilp_select_models_bic(models, verbose=False): 250 | x = cp.Variable(len(models), boolean=True) 251 | c = np.array(list(m.loglikelihood for m in models)) 252 | n_parameters = np.array(list(m._n_parameters() for m in models)) 253 | dataweights = {} 254 | for m in models: 255 | if m.name not in dataweights: 256 | dataweights[m.name] = m.weight_total 257 | n_data = sum(dataweights.values()) 258 | 259 | objective = cp.Minimize( 260 | np.log(n_data) * cp.sum(n_parameters * x) - 2 * cp.sum(c * x) 261 | ) 262 | 263 | constraints = [] 264 | for name in set(m.name for m in models): 265 | name_idx = np.array(list(int(m.name == name) for m in models)) 266 | constraints += [name_idx * x == 1] 267 | 268 | prob = cp.Problem(objective, constraints) 269 | prob.solve(verbose=verbose) 270 | idx, = np.where(x.value > 0.3) 271 | return list(models[i] for i in idx) 272 | 273 | 274 | def select_models_solo_bic(models): 275 | for m in models: 276 | m.solo_bic = np.log(m.weight_total) * m._n_parameters() - 2 * m.loglikelihood 277 | 278 | ms = [] 279 | for name in set(m.name for m in models): 280 | bestm = min([m for m in models if m.name == name], key=lambda m: m.solo_bic) 281 | ms.append(bestm) 282 | return ms 283 | 284 | 285 | def probabilities(models, X, W): 286 | weights = [] 287 | for model in models: 288 | probs = model.predict_proba(X, W[model.name].values) 289 | nextlevel_columns = list(f"{model.name}_{i}" for i in range(model.n_components)) 290 | weights.append(pd.DataFrame(probs, columns=nextlevel_columns)) 291 | return pd.concat(weights, axis=1) 292 | 293 | -------------------------------------------------------------------------------- /notebooks/1-load-and-convert-statsbomb-data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "**Disclaimer**: this notebook's compatibility with StatsBomb event data 4.0.0 was last checked on June 15th, 2020" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "%load_ext autoreload\n", 17 | "%autoreload 2\n", 18 | "import os;\n", 19 | "import warnings\n", 20 | "import pandas as pd\n", 21 | "warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)\n", 22 | "import tqdm" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "import socceraction.spadl as spadl\n", 32 | "import socceraction.spadl.statsbomb as statsbomb\n", 33 | "import socceraction.atomic.spadl as atomicspadl" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "metadata": {}, 39 | "source": [ 40 | "## Set up the statsbombloader" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "# Use this if you only want to use the free public statsbomb data\n", 50 | "free_open_data_remote = \"https://raw.githubusercontent.com/statsbomb/open-data/master/data/\"\n", 51 | "SBL = statsbomb.StatsBombLoader(root=free_open_data_remote,getter=\"remote\")\n", 52 | "\n", 53 | "# # Uncomment the code below if you have a local folder on your computer with statsbomb data\n", 54 | "#datafolder = \"../data-epl\" # Example of local folder with statsbomb data\n", 55 | "#SBL = statsbomb.StatsBombLoader(root=datafolder,getter=\"local\")" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## Select competitions to load and convert" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 4, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "data": { 72 | "text/plain": [ 73 | "{'Champions League',\n", 74 | " \"FA Women's Super League\",\n", 75 | " 'FIFA World Cup',\n", 76 | " 'La Liga',\n", 77 | " 'NWSL',\n", 78 | " 'Premier League',\n", 79 | " \"Women's World Cup\"}" 80 | ] 81 | }, 82 | "execution_count": 4, 83 | "metadata": {}, 84 | "output_type": "execute_result" 85 | } 86 | ], 87 | "source": [ 88 | "# View all available competitions\n", 89 | "competitions = SBL.competitions()\n", 90 | "set(competitions.competition_name)" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 5, 96 | "metadata": {}, 97 | "outputs": [ 98 | { 99 | "data": { 100 | "text/html": [ 101 | "
\n", 102 | "\n", 115 | "\n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | "
competition_idseason_idcountry_namecompetition_namecompetition_genderseason_namematch_updatedmatch_available
17433InternationalFIFA World Cupmale20182019-12-16T23:09:16.1687562019-12-16T23:09:16.168756
\n", 143 | "
" 144 | ], 145 | "text/plain": [ 146 | " competition_id season_id country_name competition_name \\\n", 147 | "17 43 3 International FIFA World Cup \n", 148 | "\n", 149 | " competition_gender season_name match_updated \\\n", 150 | "17 male 2018 2019-12-16T23:09:16.168756 \n", 151 | "\n", 152 | " match_available \n", 153 | "17 2019-12-16T23:09:16.168756 " 154 | ] 155 | }, 156 | "execution_count": 5, 157 | "metadata": {}, 158 | "output_type": "execute_result" 159 | } 160 | ], 161 | "source": [ 162 | "# Fifa world cup\n", 163 | "selected_competitions = competitions[competitions.competition_name==\"FIFA World Cup\"]\n", 164 | "\n", 165 | "# # Messi data\n", 166 | "# selected_competitions = competitions[competitions.competition_name==\"La Liga\"]\n", 167 | "\n", 168 | "# # FA Women's Super League\n", 169 | "# selected_competitions = competitions[competitions.competition_name==\"FA Women's Super League\"]\n", 170 | "selected_competitions" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 6, 176 | "metadata": { 177 | "scrolled": true 178 | }, 179 | "outputs": [ 180 | { 181 | "data": { 182 | "text/html": [ 183 | "
\n", 184 | "\n", 197 | "\n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | "
home_team_nameaway_team_namematch_datehome_scoreaway_score
0CroatiaDenmark2018-07-0111
1AustraliaPeru2018-06-2602
2NigeriaIceland2018-06-2220
3SerbiaBrazil2018-06-2702
4IranPortugal2018-06-2511
..................
59ColombiaJapan2018-06-1912
60JapanPoland2018-06-2801
61DenmarkAustralia2018-06-2111
62SpainRussia2018-07-0111
63CroatiaEngland2018-07-1121
\n", 299 | "

64 rows × 5 columns

\n", 300 | "
" 301 | ], 302 | "text/plain": [ 303 | " home_team_name away_team_name match_date home_score away_score\n", 304 | "0 Croatia Denmark 2018-07-01 1 1\n", 305 | "1 Australia Peru 2018-06-26 0 2\n", 306 | "2 Nigeria Iceland 2018-06-22 2 0\n", 307 | "3 Serbia Brazil 2018-06-27 0 2\n", 308 | "4 Iran Portugal 2018-06-25 1 1\n", 309 | ".. ... ... ... ... ...\n", 310 | "59 Colombia Japan 2018-06-19 1 2\n", 311 | "60 Japan Poland 2018-06-28 0 1\n", 312 | "61 Denmark Australia 2018-06-21 1 1\n", 313 | "62 Spain Russia 2018-07-01 1 1\n", 314 | "63 Croatia England 2018-07-11 2 1\n", 315 | "\n", 316 | "[64 rows x 5 columns]" 317 | ] 318 | }, 319 | "execution_count": 6, 320 | "metadata": {}, 321 | "output_type": "execute_result" 322 | } 323 | ], 324 | "source": [ 325 | "# Get matches from all selected competitions\n", 326 | "matches = list(\n", 327 | " SBL.matches(row.competition_id, row.season_id)\n", 328 | " for row in selected_competitions.itertuples()\n", 329 | ")\n", 330 | "matches = pd.concat(matches, sort=True).reset_index(drop=True)\n", 331 | "matches[[\"home_team_name\",\"away_team_name\",\"match_date\",\"home_score\",\"away_score\"]]" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "metadata": {}, 337 | "source": [ 338 | "## Load and convert match data" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": 7, 344 | "metadata": {}, 345 | "outputs": [ 346 | { 347 | "name": "stderr", 348 | "output_type": "stream", 349 | "text": [ 350 | "Loading match data: 100%|██████████| 64/64 [02:09<00:00, 2.03s/it]\n" 351 | ] 352 | } 353 | ], 354 | "source": [ 355 | "matches_verbose = tqdm.tqdm(list(matches.itertuples()),desc=\"Loading match data\")\n", 356 | "teams,players,player_games = [],[],[]\n", 357 | "actions = {}\n", 358 | "atomic_actions = {}\n", 359 | "for match in matches_verbose:\n", 360 | " # load data\n", 361 | " teams.append(SBL.teams(match.match_id))\n", 362 | " players.append(SBL.players(match.match_id))\n", 363 | " events = SBL.events(match.match_id)\n", 364 | " \n", 365 | " # convert data\n", 366 | " player_games.append(statsbomb.extract_player_games(events))\n", 367 | " actions = statsbomb.convert_to_actions(events,match.home_team_id)\n", 368 | " atomic_actions[match.match_id] = atomicspadl.convert_to_atomic(actions)\n", 369 | "\n", 370 | "games = matches.rename(columns={\"match_id\":\"game_id\"})\n", 371 | "teams = pd.concat(teams).drop_duplicates(\"team_id\").reset_index(drop=True)\n", 372 | "players = pd.concat(players).drop_duplicates(\"player_id\").reset_index(drop=True)\n", 373 | "player_games = pd.concat(player_games).reset_index(drop=True)" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": {}, 379 | "source": [ 380 | "## Store converted spadl data in a h5-file" 381 | ] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "execution_count": 8, 386 | "metadata": { 387 | "scrolled": true 388 | }, 389 | "outputs": [ 390 | { 391 | "name": "stdout", 392 | "output_type": "stream", 393 | "text": [ 394 | "Directory ../data-fifa created \n" 395 | ] 396 | } 397 | ], 398 | "source": [ 399 | "datafolder = \"../data-fifa\"\n", 400 | "\n", 401 | "# Create data folder if it doesn't exist\n", 402 | "if not os.path.exists(datafolder):\n", 403 | " os.mkdir(datafolder)\n", 404 | " print(f\"Directory {datafolder} created \")\n", 405 | "\n", 406 | "spadl_h5 = os.path.join(datafolder, \"atomic-spadl-statsbomb.h5\")\n", 407 | "\n", 408 | "# Store all spadl data in h5-file\n", 409 | "with pd.HDFStore(spadl_h5) as spadlstore:\n", 410 | " spadlstore[\"competitions\"] = selected_competitions\n", 411 | " spadlstore[\"games\"] = games\n", 412 | " spadlstore[\"teams\"] = teams\n", 413 | " spadlstore[\"players\"] = players\n", 414 | " spadlstore[\"player_games\"] = player_games\n", 415 | " for game_id in atomic_actions.keys():\n", 416 | " spadlstore[f\"atomic_actions/game_{game_id}\"] = atomic_actions[game_id]\n", 417 | "\n", 418 | " spadlstore[\"results\"] = spadl.results_df()\n", 419 | " spadlstore[\"bodyparts\"] = spadl.bodyparts_df()\n", 420 | " spadlstore[\"atomic_actiontypes\"] = atomicspadl.actiontypes_df()" 421 | ] 422 | }, 423 | { 424 | "cell_type": "markdown", 425 | "metadata": {}, 426 | "source": [ 427 | "## Plot the spadl data\n", 428 | "Extra library required: ```pip install matplotsoccer```" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 14, 434 | "metadata": {}, 435 | "outputs": [], 436 | "source": [ 437 | "# Select England vs Belgium game at World Cup\n", 438 | "with pd.HDFStore(spadl_h5) as spadlstore:\n", 439 | " games = spadlstore[\"games\"].merge(spadlstore[\"competitions\"])\n", 440 | " game_id = games[(games.competition_name == \"FIFA World Cup\") \n", 441 | " & (games.home_team_name == \"Belgium\")\n", 442 | " & (games.away_team_name == \"England\")].game_id.values[0]\n", 443 | " \n", 444 | " atomic_actions = spadlstore[f\"atomic_actions/game_{game_id}\"]\n", 445 | " atomic_actions = (\n", 446 | " atomic_actions.merge(spadlstore[\"atomic_actiontypes\"],how=\"left\")\n", 447 | " #.merge(spadlstore[\"results\"],how=\"left\")\n", 448 | " .merge(spadlstore[\"bodyparts\"],how=\"left\")\n", 449 | " .merge(spadlstore[\"players\"],how=\"left\")\n", 450 | " .merge(spadlstore[\"teams\"],how=\"left\")\n", 451 | " )\n", 452 | "\n", 453 | "# use nickname if available else use full name\n", 454 | "atomic_actions[\"player\"] = atomic_actions[[\"player_nickname\",\"player_name\"]].apply(lambda x: x[0] if x[0] else x[1],axis=1)" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": 17, 460 | "metadata": {}, 461 | "outputs": [ 462 | { 463 | "name": "stdout", 464 | "output_type": "stream", 465 | "text": [ 466 | "2018-07-14 Belgium 2-0 England 4'\n" 467 | ] 468 | }, 469 | { 470 | "data": { 471 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtAAAAD4CAYAAADFN9HuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4wLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvqOYd8AAAIABJREFUeJzs3XlYU1f6B/DvTSAssgVEhEhAICSErchWtQrue9WpSyugdcOtv7bSxbZ2s65lZrRgtYrOtB0LXabW1qGjXRFaFwSluFACqBDZFCv7GpL7+8PCiCaBYCCA7+d5eFpvTs59z7mXy5uTc89lWJYFIYQQQgghpGs4hg6AEEIIIYSQ/oQSaEIIIYQQQnRACTQhhBBCCCE6oASaEEIIIYQQHVACTQghhBBCiA4ogSaEEEIIIUQHlEATQgghhBCiA0qgCSGEEEII0QEl0IQQQgghhOiAEmhCCCGEEEJ0QAk0IYQQQgghOqAEmhBCCCGEEB1QAk0IIYQQQogOKIEmhBBCCCFEB5RAE0IIIYQQogMjQwegA9bQARBCSDcxhg6AEEKI/tAINCGEEEIIITqgBJoQQgghhBAdUAJNCCGEEEKIDiiBJoQQQgghRAeUQBNCCCGEEKIDSqAJIYQQQgjRASXQhBBCCCGE6IASaEIIIYQQQnRACTQhhBBCCCE66E9PIuySgIAAVFRUwMPDw9ChkH6ioKAAAOicITopKCiAvb09srKyDB0KIYSQXjbgEuiKigrU1dUZOgzSj9D5QrqDzhtCCHl4DbgEum0U8cSJE4YNhPQb4eHhAOicIbppO28IIYQ8fAZcAk0IIaRz586dG2JkZHQQgA/ofhhCCLmbCsCl1tbWFYGBgTfVFaAEmhBCHkJGRkYHhw4d6mVvb1/J4XBYQ8dDCCF9hUqlYioqKqTl5eUHATyurgyNOhBCyMPJx97evoaSZ0II6YjD4bD29vbVuPMNnfoyvRgPIYSQvoNDyTMhhKj35/VRY55MCTQhhBBCCCE6oASaEEJIvxEfH28XExPj1NXy77zzzpDa2tr7/tYlJiZav/baa0P1G13ndI1fVz3dXk3197QH7bfs7GwTiUQiNTc3D0hLSzPXZ2wPM339PvZHA6IRhBBCiDr79+93qKuru+9vXURERPW2bdvKDRFTT+rp9mqqv6/z9/dvzs3NzfHx8WkwdCwPs/56/qgzIBpBCCGE3O3rr7+2lEgk0ps3bxqHhYV5SiQSaWFhoTEAzJ0719XR0dF38eLFwrbyMTExTsHBwWKhUOgTFRUldHV19SktLTUCgKSkJGtfX18vsVgsXbFixbDeasP48eM9xGKx1MfHx2vbtm32bdvlcrlR22ve3t5eFy5cMNG1vQCwZcuWISKRyFskEnnHxcXZtW03NzcPWLJkibOrq6vPk08+6QJo7s+kpCTrxx9/fHjbe9evX++0adOmITKZjCcUCn2mT5/u5u7u7r1x48b20e+e7E9t+9VEXT9rape2+JOTky1HjRolmjp1qptIJPJetmyZsz7b1p9pOz819ae646Lt97S3UQJNCCFkwJkzZ05tbm5uzpAhQxSpqal5ubm5Oa6urgoAOHLkSOGrr75aeu97pk+fXjVhwoRqNze35vDw8OqUlJRBJSUlRtu3b3c6deqUTCaT5ZSUlPCOHj1q2RttOHDgQJFMJss5d+5cbkJCgoNcLjcCgFWrVgmnTJlSLZPJcn755ReZnZ2dUtf2ymQyXkJCwpDMzMzfT506lbtjxw6nkpISIwBobGzkREZG3pbJZJdTU1OtCgsLjTXVP3/+/Orz589b1NTUcADgm2++4S9btuw2ABQXF5ts2bKlNDs7O+eTTz4ZnJeXx+uN/lS3X137WVO7Oos/KyvLYvPmzaX5+fmXY2Nj7zvHHlaazh9t/anp/Ff3e2qINtE60IQQQggAW1vb1pqaGm7bf6uqqrgnTpwYVFxczAsODpYAQENDA6egoMAEQG1Px7Nnzx77Y8eO2bAsi5s3bxrL5XKeUChsPXPmjOWRI0eu/RmzCnce+qCT9PR085CQkDpra2sVAPj7+9dnZGSYCQSCWmNjY3bSpEn1ADBs2LCW4uJi47Zk/F7GxsaYMmVKVWJioo1EImkSCoXNzs7OrTKZjOfo6NgyYsSIJgAIDg6uO3PmjDmXy2V7uj/V7dfT07NFU3lN/ayuXYmJidba4pdKpQ3BwcFNADB48GClvto0UGn7/VJ3XAD1v6eGiJ0SaEIIIQQAwzAdfpRKJQMAY8aMqTl69Oi13owlOTnZMiUlxers2bO5lpaWKh8fHy+VSuc8uVuMjIzalzdkGAad7Xf58uW3Xn31VYGbm1tzRETEH53Vb4j+bMMwTIelG7X1s6Z2aYvfysqKkmYdqetPbcdF0+9pb6MpHIQQQgasQYMGKSsqKro9QhUeHl6fkZFhceXKFWMAyMvL47V9ldyTqqurOXw+v9XS0lKVmZlpmpuba9b22siRI2t3795tBwA1NTWcu+eAdrW9oaGhDRkZGRY1NTWcW7ducS9cuDAoODi4sbP3qat/1KhRjbdu3TL+4YcfbJ566qmqtu1lZWW87Oxsk4aGBiYzM9MiNDS0oTf6U91+217j8/mtRUVF7VM6tPWzunYZ6nwYKO49fzT1p7bj0lfQQSeEEDJgrVq16ubcuXM9bGxsWo8cOXKlsbGRM3v2bI/q6mpuU1MTRyKRWGzatKlE0/sFAkHrrl275DNnzhQplUqYmZmpkpKSrgFo7cm4n3jiiZqDBw/au7u7e7u7uzdJpdL2JHDfvn3yp59+2vXgwYNDjI2N2aSkpKtOTk6turR34cKF1StXrrwZGBjoBQAvv/xyqUAg6LRN99YvFApbAWDmzJmVeXl5phYWFu0jvAKBoPnFF18cdvXqVdPIyMgKsVjcAgA93Z+a9gsAL7zwwo2VK1cO37p1q9Px48fztfWzunYZ6nwYKNSdP+r6s7Pj0hcwLNtvHkTVpUDDw8MBACdOnOjBUMhAQucM6Q4dzxuDfMWoTXZ2dqG/v/8tQ8ehq/j4eLvCwkKTnTt39ssbtPp7/JqEhYV5rF+//sacOXNqgTs3Kc6cOVOUn59/WR/1d7Xf9L3fe9tFOhqo53Ob7Ozswf7+/q7qXqMpHIQQQgjplvLycq6rq6uPnZ1d6+OPPz5gksyB2i6iPzSFgxBCSL8RFBTU4ObmpnFFhb6uv8d/r6FDhyoLCwsv3btdLBa36GsUGOh6v+lrv5raRToaaOezLiiBJoQQ0m+MGjWq0xvd+rL+Hr+hUL/1TQ/zcaEpHIQQQgghhOiAEmhCCCGEEEJ0QAk0IYQQQgghOqAEmhBCCCGEEB1QAk0IIYQQQogOKIEmhDz0qqqqMG/ePEgkEnh5eeH06dOGDokQQkgfRgk0IWTAqa+vx+tvvIkhQx3xxptvoaFB+1Ngn3vuOUydOhW5ubnIzs6Gl5dXL0VKCCGkP6IEmhAyYLAsi08//RQenhJ8eyoLs16JQ/LJ83AXifHpp5+CZVm4urrC19cXZmZmsLGxQXV1NdLS0rB8+XIAAI/Hg42NjYFbQgghpC+jBJoQ0q8plUrs3LkTg+2HYLiHJ17ZtA1TY97F7A074SwNwOwNOzE15l28smkbQkeNRnNzM+bPn4+5c+fisccew7Vr12Bvb4+lS5ciICAAK1asQH19vaGb9dD4+eefB0kkEqlEIpH+8MMPgzRto+20nbbT9t7Y3lUMy7K6vsdQuhRoeHg4AODEiRM9GAoZSOic6b/y8/MRteRpVNbWo+hKASavehWPTH4CHC73vrIqpRLZP3yF/+55B4/4+eLdd9/Fzp078fbbb+PRRx/FyZMnERoaiueeew5WVlbYvHmz1n3reN4wOjeuh2VnZxf6+/vfMnQchBDSV2VnZw/29/d3VfcajUATQvqdtlHnkNBHIR0zFUtf3YHBw1wxYtoCtckzAHC4XARMnQ9LuyGoqKjA6tWrIZfLMWzYMAwbNgyhoaEAgHnz5uH8+fO92RxCCCH9jJGhAyCEEF20jTrXtyjxzkdH4ejihqu/X7ivXPmVHFjaDcUgG9sO242Nefjqqy9QVFSEJUuWIC8vD87OzpDJZBCLxfjpp58glUp7qzmEEEL6IUqgCSH9QmFhIX799Vesj4nBhHmL8ZeV68Hh/O9LNJZVdSh/8LkFsHfxwKo9X3fY3tqqwIwZM8CyLBobG/H888/jn//8JyIiItDS0gI3Nzd8+OGHvdImQggh/RMl0ISQfmHv3r3YtWsXWJbFF/t34b+ffggnV3e4S/3BsirU19Z0KG9qYYXygpz76rG0tMRXSf/CjRs3sGjRImzbtg2PPPIIMjMze6sphBBC+jlKoAkh/cKcOXOwb98+1NbWAgBqq25D9tttyH7LAABY8O07lHf1DcbltGP31VNXV4+FCxdCoVBgyJAhmDp1as8HTwghZEChmwgJIf2Cvb19e/J8N56pKWZErISiuQHnj30BlVIJABD6Bncop1Iqcf7YF2iq/gPfffcdCgsLIZPJeiV2QgghA8uAG4HOzMyEQqFoX2KqrysoKIC9vT2ysrIMHQohBhMQEICKigp4eHi0b2NZFhUVFcjPz0dra6va9/FMTPH0i5swaV4Uxkz/C/7x7hs4l5yEyas3wtUvpL1c0cUMfBv/JlobaiD2FGHZsmUPHPPp06dhbGz8wPX0F62trdiyZYvD/v0JjqtXryrbuHHjDSOjAfcnhBBCumTAXf0UCgWUf45A9Qd1dXWGDoEQg6uoqEBdXR0UCgUKCwtRWlra4XWBQAAXFxfU1tYiJycHSqUSXCMjPLvtfYROmA4AcPf2x9aPv8Gp777Bv/76EoZ4eAMAEl9bhhJZNlydh8He1QsMo58lmfvTdeZBXbx40eTpp5e6cY15pn/fc5DzXuwWp//8J9n2ww//edXX17fZ0PERQkhvG3AJtJmZGYD+81CM/jJSTkhPOX36NKqqqlBfX49Tp04BAGxtbfHBBx9g/vz5HRLelpYW2NjYgGVZDHPzbE+e2zAMg9FT5yAobDJeWjARAOA/fCiy076Dubm5XuN+GB733TbqHBcf7/T8SxuZpdHrGA6Hg9Fjx3E+TNhjNjYsTPr8c8+Vvv766ze4Gtbf1iY6OnrY4cOH7WxtbVvz8/Mvd+U98fHxdoWFhSY7d+4s1VauvLycO3HiRE+FQsEwDIONGzeWRkVFVXWn/MGDB/mbN28WMAyDbdu2XV+0aFG1bi3VPf7ekpiYaH358mWzbdu2lXfn/dnZ2SYLFy50l8vlJsePH5eNHTu2Qd8xAl3vt+TkZMunnnrKXSAQtLS0tDCrV6+++fLLL1foKw59ttfc3DygoaGhy18/CwQC38zMzN8dHR3VfyX3gOW7ozvHRalUMn/5y1/+ePfddzWec93t5wc9n3Ux4BJoQkjf1tLSgvj4eLz00ksdttvZ2SE9PR3u7u4a38vj8fDFF1+Az+dj7l+ewPxHnDSWtR/iAAAY6uCg9+T5YXD3qPPR73/hDHcXtb/G5XKxYs2zzITJ05iYdSu6PRo9f/78ysjIyNvLly8fru/4+Xy+6uTJkzJra2tVWVmZkZ+fn3TRokVVmhJ9TeUVCgXz1ltvDTtz5szvTU1NnPHjx3suXLiwujsfGPqiiIiIagDd/kDg7+/fnJubmxMSEiLWY1gPJCgoqC4lJaWgtLTUyNvb23vBggVVrq6uCn3U3Rfb21+0HReFQgGRSOSzfPny256eni3qyna3nx/0fNYF3URICOlxV69exZw5c8AwDExMTNqT59jYWDQ3NyMsLAw+Pj5ak+c2M2fOxOjRo3HzRjlYltX4c/PGnQGI1NTUHm3bQDV58mTJ5BlzzL789ucOyfPdhruL8OW3P3MmTZ9tNnnyZImu+5g0aVK9g4NDj4yOmZiYsNbW1ioAuH37NrelpYWjUCiYmJgYp+DgYLFQKPSJiooSurq6+pSWlhppKn/ixIlBnp6ejc7Ozq0ikajF0dFRkZ6ebgYAmzdvHuLm5ubt6ekpXbNmjUDfbUhOTrYcNWqUaOrUqW4ikch72bJlzgCQlJRk7evr6yUWi6UrVqwY1lZeLpcbjR8/3kMsFku9vb29Lly4YKKt/Ny5c10dHR19Fy9eLGzbtm/fPtu7/71s2TLn999/3w4A2ur28fHx2rZtW8dlb/ogJyenVqFQ2FxUVGQMAFu2bBkiEom8RSKRd1xcnB0AaDofAM39pom5uXlA2/+HhISI09LSuvXJvbN6Ll++bCKVSr0uXrxoAnR+XO4tr684u6u2tpajUCgYU1NTFaB7P7/xxhsOw4cP9x4/fryHs7Ozj0wm4wHqz2dAfXu1HfeuohFoQojesSyLw4cPY82aNbh161b7dn9/f+zZswejR4/ulTgsLCxw6dKlXtnXQOM63K1FJPEyv/thNepwuVx4iCXMcDe3PjcXurKykhMaGiq5fv26SVxcXKGpqSkLANOnT6+Sy+U8oVDYwuVyq1NSUgZFRERUqytfWlpq7ODgoIiNjbW3tbVtHTJkiKK4uNgYQGNsbKyTXC6/wOfzVSUlJT3y9zQrK8siLS3t9+Dg4KZbt25xS0pKjLZv3+506tQpmaWlpWratGluR48etXz88cdrV61aJZwyZUr1hg0bKm7fvs1pbGzkaCt/5MiRwvj4eLvMzMxBbftbsGBB1datW51UKhU4HA5+/vln69jY2FIAOHDgQJG7u7uiubmZEYvF3pGRkZVCobDHpgc8qIKCAmMACAkJaZTJZLyEhIQhFy9ezGlpaWH8/Pyk8+bNqwbUnw/h4eH1mvrNkG26evWqcXR0tOtHH31U2PaNj7bjoq68oWRmZlpIJBKpXC43iY6OviEUClu1nZ/q6sjPz+d9/PHH9pcuXcq5cuUKLzAw0LvtNXXnszaargNdbQ8l0IQQvaiqqsLbb7+NuLi4DtvXrVuHd955B7a2thre2XPCwsLw7bff9vp+B4KJE8bf/vn7Y6aTp83q9JvKn384rpo4YUJlb8SlCz6fr8rLy8s5f/686cqVK12WLFlSBQC2tratNTU13Lb/VlVVcbWVB4C2ebRff/01v22br69v/aJFi1ynTZtWHRERoXF+9YOQSqUNwcHBTQAwePBgZWJionVxcTEvODhYAgANDQ2cgoICEwC1Z86csTxy5Mi1P9uoAqDSVl7d/mxtbVUeHh5Nqamp5jwejx02bFjz4MGDlQCwZ88e+2PHjtmwLIubN28a/5l89LkEOjMz08LDw8O7tLSUl5iYeMXMzIxNT083DwkJqWv7lsHf378+IyPDDFB/Ppw4cWKQLv3WW+bPn+8xfvz46pCQkMa2bdqOi7ryhtI2haOyspIzduxYcXp6emVBQQFPl34+ffq0eUhISJ2VlZUqICCgydHRUe0UkK7QdB3oKkqgCSHdlp6ejnXr1uHcuXPt22xsbLB37148+eSTelvxorsoge6+OXPmVD8+e44Ty7JajyPLskj54Thijn7TK/MOu2PEiBFNxsbGbNvUC4ZhOvwolUpGU3mBQKC4ceNG+3qFN2/eNB42bJgCANLS0vK/++47i6SkJNv9+/cPuXz58u/6jt3Kyuq+5V7GjBlTc/To0WtdrUPX8nPnzq388ssv+Twej50zZ04lcGc6SUpKitXZs2dzLS0tVT4+Pl4qlar9PQzDsF2tv6e1JWqffPKJzdq1a12mT5+u9WsoTeeDtn5T1967f08eZJUebfXs3r276NVXXx2WkpJiPm7cuIbOjsu95fUZZ3fx+XzVmDFjalJSUiycnZ1bdO1nXWlqb2fXgc7QHGhCSJcpFArs2rWr/YLz6KOP4ty5c5g5cyby8vLAsiwqKyvx1FNPGTx5BoCxY8caOoR+KyAgoIllVap8Wa7Wcnm5v4NhoAoICGjqpdC65Nq1a8bl5eVc4M7c4IKCAjMXFxeNN5JpKh8WFlafl5dnVlJSYlRQUGBcXl5uHBoa2qhSqVBQUMCbMWNG3c6dO0tKS0t5vdGu8PDw+oyMDIsrV64YA0BeXh5PLpcbAcDIkSNrd+/ebQcANTU1nNLSUiNt5TVZuHBh1Q8//GB9/Phxm6eeeqoKAKqrqzl8Pr/V0tJSlZmZaZqbm2t293v4fH5rUVFRr/RBV0VGRlaJxeLGhIQE29DQ0IaMjAyLmpoazq1bt7gXLlwYFBwcrHFUtrN+U9deCwsL5Y0bN7h1dXXM1atXTbsbt7Z6Ro0a1bB3796iVatWuTY2NjKdHZd7y+szzu5SKpW4ePGiuYuLS4uu/Txy5MiGjIwMi9raWk5WVpZpWVlZp+dcT7WXRqAJIVoVFhbihRdewFdffdVh+44dO/D888/DxMTEQJF1bsSIEQCA6upqWFtbGzia/oXD4WBceHh1yo/H7TwlXhrLpfx4HOHh4VWdzZVWJyoqSnj8+HGbyspKIwcHB7+dO3cW6TIHUZsrV67w1q5d6wIASqWSeeutt4q1JdDayr/99tvFo0aNkgDAjh07rnO5XCiVSkRERAyvra3lqlQqZtOmTcX6iLszAoGgddeuXfKZM2eKlEolzMzMVElJSdcAtO7bt0/+9NNPux48eHCIsbExm5SUdNXPz69ZXXmZTMaZPXu2R3V1NbepqYkjkUgsNm3aVLJw4cJqBwcHpb29vUKhUHAEAkErADzxxBM1Bw8etHd3d/d2d3dvkkqlHZYVe+GFF26sXLly+NatW52OHz+er69VLx7Uxo0by9atW+eybt26P1auXHkzMDDQCwBefvnl0ra2qaOtnwH17X322WfLJ06c6Onv718/dOjQTtvf1NTEcXBw8Gv79/r168teeeWVis7qmThxYv2YMWNq169fL3jvvfdKtB2Xe8vv27evWNc49aVtDrRSqWRGjx5ds3DhwmoOhwNd+lkkErUsXry4ws/Pz0skEjUJBIJmU1NTViaT8TSdzz3VXoZl+8y3Lp3pUqBta7NWVfXIdDS9a1sHur+sWz0Q0THoiGVZHDlyBGvWrMHNmzfbt/v6+mLPnj0YM2aM3vfZk8eAYRgkJydjxowZeq1Xx2uN4Yfj75GdnV3o7+9/S1uZTz75xGZfwj9cP/vmO41zAxc+Plm5dvXKa/pKfDvT19ZR1lV/j99QqN/6JkMcl8rKSg6fz1eVlZUZBQYGehUXF1/szgf4rsjOzh7s7+/vqu41GoEmhKC6uhqbNm3Crl27Omxfs2YNNm/eDDs7OwNFph+pqal6T6AfBrNmzap5fv16ONtq/pbBbvBgzJw506A3VhFCHh6rV692zsrKGgQAsbGx13sqee4MJdCEPKQyMjKwbt06ZGRktG+zsrLCBx980GfmMOtLWlqaoUPol6ytrVW3Kip+M3QcdwsKCmpwc3Pr9p33htbf4zcU6re+yRDH5fPPPy/qzf1pQgk0If1YU1MTxo4di+bmZrS2tmLevHnYtGmT2rIKhQJ79+7F888/32H79OnTsWvXLnh6evZGyL2Ow+EgPT3d0GEQPRk1apTBl+N6EP09fkOhfuubHubjQqtwENKPmZiY4Oeff0Z2djZ+++03HD9+HGfOnGl/XS6XY8GCBWAYBjwerz153rZtGxobG8GyLL799tsBmzwDd5ayI4QQQvSJEmhC+giWZZGeno6oqMXg823B5XLB59ti8eIlOHv2LNTd8MswDCwsLADcGWFuaWlBWloanJycwDAMXFxc8O9//xve3t44ceJE+2OuX331VZia9vrqRQZBCTQhhBB9oykchPQBCoUCK6Oj8dNPKXgiYjk+O34afDt7VP5RgWNff4En5i3AhAnjcCAhAcbGxh3eW1lZCbFYjIqKCgDAb7/dmbIaHR2NrVu3YvDgwb3enr6E1oImhBCibzQCTYiBsSyLldHRKLhWjM+On8biVc/B3sERRkZGsHdwxOJVz+Gz46eRf/U6VkZHg2VZnDt3DiNHjgTDMLC1tUVFRQUsLCwgkUiQnZ0NlmWxf//+hz55BoDQ0FAAQGPjQztVjxBCiJ5RAk2IgZ09exY//ZSCd/f+C2bmg9SWMeaZIPSxcfj4o4/A4XAQFBSEM2fOYOrUqfj999/Bsixqa2vx1FNP4fvvv+/lFvRt5ubmANBhbjgxvGvXrhkHBgaKPTw8vKVSqdeRI0esuvK++Ph4u5iYGKfOypWXl3N9fHy8xGKxVCKRSA8dOmTT3fLR0dHD7Ozs/EUikXdXYtRH/PcSiUTeMpms06euJSYmWr/22mtD795mbm4eoK6suu3Z2dkmEolEam5uHpCWlmaua5w9pav9lpycbGlpafmIRCKRurm5ecfGxtrrMw599o+m46KJQCDwLSsr6/LMAV3Ld0d3jotIJPLesGHDUG3lu9vP6s7/nkJTOAgxsD179uKJiOX3Jc/lpcWI2/4Gvk/u+ATARx55BKdPn4apqSkqKirap3Q0Njbihx9+wIYNG3ot9v4kNTUV48aNM3QY5E9GRkbs+++/Lx85cmRjXl4e77HHHpPMnTv3gr7q5/P5qpMnT8qsra1VZWVlRn5+ftJFixZVcbnqnwmjrfz8+fMrIyMjby9fvny4vuLrKX8+0KbbD7Xx9/dvzs3NzQkJCRHrMaxeFRQUVJeSklJQWlpq5O3t7b1gwYIqfT0ZcSD0j6G0HReFQgGRSOSzfPny256enmqXwOtuPz/o+a8LGoEmxMD+85//YNqcBR22Xc4+hxmjvfF98ldwdffEB4lHce5aNY6d/h1FRfL2GwDLysowbtw4+Pn5ITg4GJMmTcLMmTMN0Yw+j9aC1o5hmEBNP3/729/a5wL97W9/G6ytbFf35+zs3Dpy5MhGAPD09GxRKBRMY2Oj3hYfNzExYa2trVUAcPv2bW5LSwtHoVAwMTExTsHBwWKhUOgTFRUldHV19SktLTXSVB4AJk2aVO/g4HDfY583b948xM3NzdvT01O6Zs0agb5ib/P66687uLm5eU+bNs2tubm5vW+Sk5MtR40aJZo6daqbSCTyXrZsmTMAzJ0719XR0dF38eLFwrvrYVkWTz75pItIJPJeunSpc2fbNUlKSrL29fX1EovF0hUrVgzTZ1t7gpOTU6tQKGwuKioyBoAtW7YMEYlE3iKRyDsuLs4OADSdD4BSGh+XAAAgAElEQVTu7b17RDkkJETc3RHqzuq5fPmyiVQq9bp48aIJAIwfP95DLBZLfXx8vLZt23bfiPu95fUVZ3fV1tZyFAoFY2pqqgJ07+c33njDYfjw4d7jx4/3cHZ29mn7ZkbT+a+uvdqOe1fRCDQhBlZTUw2+Xcdrnrd/IH7NKYOZWcfrGt/OHjU1//tw7efnh6ysrF6Js79LTU01dAhEg8OHD1t5e3s3mJmZ3b/UzAOorKzkhIaGSq5fv24SFxdXaGpqygLA9OnTq+RyOU8oFLZwudzqlJSUQREREdWaymsSGxvrJJfLL/D5fFVJSYle/57KZDLeoUOH7C9fvnw5NzfXJCQkpMP0kaysLIu0tLTfg4ODm27dusUFgCNHjhTGx8fbZWZmdvg6q6mpiTN//vzKxMTEopCQEPHx48ctpk6dWqdpu7p4SkpKjLZv3+506tQpmaWlpWratGluR48etXz88cf77FMoCwoKjAEgJCSkUSaT8RISEoZcvHgxp6WlhfHz85POmzevGlB/PoSHh9f3xfZevXrVODo62vWjjz4q9PX1bQaAAwcOFLm7uyuam5sZsVjsHRkZWSkUCls1lTeUzMxMC4lEIpXL5SbR0dE3hEJhq67nVX5+Pu/jjz+2v3TpUs6VK1d4gYGB7b8Xms5/TTRdB7raHkqgCTEwKytrVP5RAXsHxw7b702eAaDyjwpYWVn3VmgDRkhICM6ePWvoMPo0lmXPdaXciy++eOvFF1+8pa/9yuVyo1deecX566+/LtBXnW34fL4qLy8v5/z586YrV650WbJkSRUA2NrattbU1HDb/ltVVcXVVN7ExERjEu3r61u/aNEi12nTplVHRERU6TP2M2fOmAcHB9dZWFiwQUFBTY6Ojh2+6pZKpQ3BwcFNADB48GCltrq4XC7mzJlTw+VyER4eXpOenm4+derUOk3b1dVx4sSJQcXFxbzg4GAJADQ0NHAKCgpMAPS5BDozM9PCw8PDu7S0lJeYmHjFzMyMTU9PNw8JCalr+5bB39+/PiMjwwxQfz701fbOnz/fY/z48dUhISHtd0Xv2bPH/tixYzYsy+LmzZvGfyaFrZrKG0rbFI7KykrO2LFjxenp6ZUFBQU8Xfr59OnT5iEhIXVWVlaqgICA+34vdKHpOtBVNIWDEAObNWsWjn39RZfKHvv6C8yaNauHIxp4aC3ovqmhoYGZO3eu+/bt2697e3v32OjYiBEjmoyNjdn09HQz4M766Xf/KJVKRlt5TdLS0vKfeeaZm+np6YPGjh2r16cRMYz22SxWVlZak+bu1H33doZh7vvgMGbMmJrc3Nyc3NzcHLlcfikmJkZvH6T0KSgoqK6goOByQkLCtbVr17rcPf1FHU3ng7b2quufu/tPqez24dFaz+7du4vOnj1rkZKSYg7cmc6TkpJidfbs2dzc3NwcNze3JpVKpbG8PuPsLj6frxozZkxNSkqKBaB7P+tKU3s7uw50hhJoQgxs3bq1OJz4DzQ21Gst11Bfh8NJ/8C6dWt7KbKBgxLovkelUmHhwoWu8+fPvz1v3rwafdd/7do14/Lyci5wZ5S7oKDAzMXFReONZLqWV6lUKCgo4M2YMaNu586dJaWlpZ2ukKGLRx99tCEzM9OioaGBOXfunGlZWVm361cqlTh69KiVUqnEiRMnrB599NF6bdsBgM/ntxYVFbXvMzw8vD4jI8PiypUrxgCQl5fHk8vlffpb7MjIyCqxWNyYkJBgGxoa2pCRkWFRU1PDuXXrFvfChQuDgoODNY7Kdtbee/sHACwsLJQ3btzg1tXVMVevXu32k6q01TNq1KiGvXv3Fq1atcq1sbGRqa6u5vD5/FZLS0tVZmamaW5urpm28vqMs7uUSiUuXrxo7uLi0qJrP48cObIhIyPDora2lpOVldWl34ueai8l0IQYWEhICAQCRzyz5C8ak+iG+jpsWLsYEyeMR3BwcC9H2P+NHj0awJ0H1pC+4YcffrA4duwY/6OPPrKXSCRSiUQiLSwsNO78nV1z5coV3tixY8Wenp7ScePGid96661ibQmxtvJRUVHCxx57THLt2jUTBwcHv8TERGuWZRERETHc09NTOnr0aMmmTZuK9RU7cOfGysjIyAofHx/pa6+9JhAIBFpH6GUyGU8ikUi3b9/u9J///IcvkUikn3/+uTUAmJqaqpKSkmwlEom3n59fw+TJk+u1bQeAF1544cYbb7wxrO24CASC1l27dslnzpwp8vT0lM6fP9+ttrZWp6+8DWHjxo1lu3fvdhCJRC0rV668GRgY6DVy5EjJyy+/XCoQCO67MbRNZ+29t38A4Nlnny2fOHGi5+rVq52HDh3a6cWmqamJ4+Dg4Nf2s2PHDvuu1DNx4sT6MWPG1K5fv17wxBNP1LAsC3d3d+/XXntNIJVKG7SV706c+tI2B1oikXi7u7s3LVy4sFrXfhaJRC2LFy+u8PPz89qwYYNAIBA0m5qastrO/55qL6Pu8cB9VJcCtbG5s3RnVZVep6P1mPDwcADAiRMnDBrHw8zQxyA3Nxf+/v4Aw8DOzh7zo1Zi2pwFHZ5EeDjpH5g4YbzaJxEOBL1xDBiGQXp6OkJCQvRSn47XGr2tLqEv2dnZhf7+/n3yK3ht4uPj7QoLC0127txZauhYuqO/x28o1G99kyGOS2VlJYfP56vKysqMAgMDvYqLiy9yOD0zHpydnT3Y39/fVd1rffrrF0IeFj4+PvD19cXatWvx/vt7sGj6aNTUVMPKyhqzZs3CkcNf0sizHqSmpuotgSaEENL7Vq9e7ZyVlTUIAGJjY6/3VPLcmQGXQNfW1oJl2fYRLV0UFBTA3t6elgV7CLAsixs3buCPP/5Abe2dm30vXLgAMzMzuLi4gMfT63RGrSQSCc6cOYOWlhYMGjQI//oXJXg9JTU1FS+99FL7vwMCAlBRUQEPDw+d66qpqen0Ri+if0FBQQ1ubm7dvvPe0Pp7/IZC/dY3GeK4fP7550W9uT9NBlwCzbIsujstpa5O7eo9ZAC4desWTpw4gaysLJw/fx5ZWVlQKBRwcHDA9evXAQCRkZGoq6tDeXk5vLy8EBAQgBEjRiA0NBQjRozQe7JUXV0NKysrMAwDY2PjATk1o6+592EqFRUV3f6970fT3waUUaNGGXw5rgfR3+M3FOq3vulhPi4DLoFuG8rvzlzK7oxak76LZVmkpqZi//79OHbsGB577DEEBgZi9erVCAgIgLOzMxiGuW/+bX19PS5cuIDz58/j/Pnz2LlzJ0xNTREdHY2oqCjw+fwHjk2hUGDcuHEYPnw4EhISYGdn98B1Eu18fHxw6dKlDtvaRp67c70wMhpwl09CCCFdRH8ByIDT0tKCDz74AHv37oWRkRFWrVqFPXv2wNbWtkvvHzRoEEaOHImRI0cC6JiIv/nmm5g9ezZeffVVSCSSbse4detWZGVlobKyslenizzMwsLC7kugSedUKhVOnDgxaPf7exx+/ukn67q6Wo6FhaVqwsSJ1f/3zLrysLCwBkPNQSSEEEOhqx4ZUDIyMhAUFITjx4/jww8/xKVLl/Dss892OXlWp22U+tNPP0V+fj68vLzw2GOPYevWrd1aFu38+fPYunUrAODDDz+EpaVlt2MjXUdrQeuuubmZWbBw4fCoJU97CsWP2HzxfTrntKwCX3yfznH29LeJXLxEvGDhwuGdPaiCEEIGGkqg+4DmZoM+nn5AqK+vxwsvvIBZs2bhlVdewX//+1+MGjVK7/OW7e3t8corr+D8+fM4efIkAgMDkZGR0eX3Nzc3Y/HixWhtbcWzzz5L04b+1NLS0uNziseMGQPgzogq6ZxKpUJEZKRr6c3bNl98l85Zsuo5xt7BEUZGRrB3cMSSVc8xX3yXzim58YdNRGSkq679Wl5ezvXx8fESi8VSiUQiPXTokE1X3hcfH28XExPjpO/6NZXvbpwPGj/pqKv9lpycbGlpafmIRCKRurm5ecfGxtrrM47s7GwTiUQiNTc3D0hLSzPv/B2amZubB+hSXiAQ+JaVlXV55oCu5bujO8dFJBJ5b9iwYai28t3t58TEROvXXntNa936Qgl0H5CTk4Po6GhKpLspLy8PjzzyCG7cuIGLFy9i0aJFPb46glAoxLfffosNGzZg1qxZiI2N7VIC+Pbbb+Py5csQiUTYvn17j8bYX/z444/IzMxETY3eH0bXwdChd66pOTk5PbqfgeLEiRODTp9Jt/nrB59wzMwHqS1jZj4If9uXyDl9Jt0mNTVVp2SCz+erTp48KZPJZDkpKSl5MTExQn0+VljX+jWV7+k4if4FBQXV5ebm5vz666+y7du3O+nzAT3+/v7Nubm5OT4+Pvc9sIRo13ZccnJyLn/++eeD8/LyNM5f7G4/R0REVG/btq38waPtHCXQfYCfnx9u376NsLAwVFdXGzqcfiUjIwNhYWHYsGEDPvnkE9jb63WwQSuGYRAREYFz587h0KFDWL9+vdbRzczMTMTGxoLD4eCjjz6CufkDDV4MCPv370dUVBSkUimsra17ZZ/3rsRB1Ht/z16HeZErGU3Jcxsz80GYF7GCeX/PXp1GfUxMTFhra2sVANy+fZvb0tLCUSgUevvkq6n+mJgYp+DgYLFQKPSJiooSurq6+pSWlhppKq8tzs2bNw9xc3Pz9vT0lK5Zs0agr9iBO08WFAqFPtOnT3dzd3f33rhxY3v/jh8/3kMsFkt9fHy8tm3b1n7R0xRPT8bZlzk5ObUKhcLmoqIiYwDYsmXLEJFI5C0Sibzj4uLsAEDT+QAASUlJ1r6+vl5isVi6YsWKYZ3t7+4R5ZCQEHF3R6g7q+fy5csmUqnU6+LFiyaA5vNBU3l9xdldtbW1HIVCwZiamqoA3fv5jTfecBg+fLj3+PHjPZydnX1kMhkPAObOnevq6Ojou3jxYuHd5dW1V9tx7yq6ibAP4HK5+Pe//41169ZhwYIFSE5OpiXNuiAzMxMzZszAgQMHMHv2bIPFIRAIkJaWhpkzZ+KZZ57Bnj171I6AS6VSPPfcc+DxeBg1apQBIu1bjh49infeeQenTp3C0qVLe22/qampWLt2ba/tr7/66ccfrb94eWuXEtppcxYwT07dpfMnoMrKSk5oaKjk+vXrJnFxcYWmpqZ6ncejqf7p06dXyeVynlAobOFyudUpKSmDIiIiqjWV17Q9NjbWSS6XX+Dz+aqSkhK9/z0tLi42+frrrws8PT2bvb29vZcsWXLb09Oz5cCBA0Xu7u6K5uZmRiwWe0dGRlYKhcJWTfH0dJx9VUFBgTEAhISENMpkMl5CQsKQixcv5rS0tDB+fn7SefPmVQPqz4fw8PD67du3O506dUpmaWmpmjZtmtvRo0ctH3/88VpDtunq1avG0dHRrh999FGhr69vMwBoOh80lTeUtkd5y+Vyk+jo6BtCobC1pKTESJd+zs/P53388cf2ly5dyrly5QovMDDQu+21I0eOFMbHx9tlZmZq/9T/J03Xga62h0ag+wiGYRAfHw8jIyOsW7eO1pjtRE5ODmbOnImDBw8aNHluw+fzcezYMZw7dw4vv/yy2jLm5ubYuXMnTd0AcO7cOSxfvhxff/01hg8f3qv7Tk1N7dX99Vd1dbUcvl3XvtHh29mjrq5W578nfD5flZeXl3Py5Mnf9+/fP0TfNyNqqt/W1rbVxsZGaWtr28rn85VVVVVcbeU1bff19a1ftGiRa3x8vJ2+k38AcHR0bBkxYkSThYUFGxwcXHfmzBlzANizZ4+9l5eX1N/f3+vmzZvGcrmcpy2eno6zr8nMzLTw8PDw9vPz83n77bdLzczM2PT0dPOQkJA6a2trlb29vdLf378+IyPDDFB/Ppw4cWJQcXExLzg4WCKRSKSXL182LygoMDF02+bPn+8REBBQHxIS0r7+sqbzQVN5Q2mbwlFSUpL9008/Waenp5vp2s+nT582DwkJqbOyslIFBAQ0OTo6dvshLpquA11FCXQfYmRkhM8++wwZGRn461//auhw+qzm5mY8+eST2Lp1Kx5//HFDh9POysoKx48fx5dffonk5OT27Tk5OR2m5rSNTl+/fh2OjnduyjI1NUVcXFyvx2wIcrkcs2fPRkJCQq8/ntzNzQ03btzo1X32VxYWlqrKPyq6VLbyjwpYWFh2++7MESNGNBkbG7Pp6elm3a1Dl/oZhunwo1QqGW3lNW1PS0vLf+aZZ26mp6cPGjt2rGdPxH6v5ORky5SUFKuzZ8/m5ubm5ri5uTW1TR3TFI8h4jSkoKCguoKCgssJCQnX1q5d69LZBzNN58OYMWNqcnNzc3Jzc3PkcvmlmJiYW3e9574PInd/8/gg8+S11bN79+6is2fPWqSkpJgD2s8HdeX1GWd38fl81ZgxY2pSUlIsAN37WVea2tvZdaAzlED3MZaWlkhOTsbu3bvx1VdfGTqcPunNN9+Eh4cHli1bZuhQ7mNlZYUZM2Zg4cInsXnLFlRXV2PWrFnw9fVFXl5eh7JGRkbYvHkzzp49i+HDh2PPnj0D/ga3uro6zJgxAzExMZg7d26v73/s2LG9vs/+asLEidX//fqLLv3xOvb1F+yEiRN1uoHj2rVrxuXl5VwAkMvlRgUFBWYuLi66rwupp/o1lde0XaVSoaCggDdjxoy6nTt3lpSWlup9QfeysjJedna2SUNDA5OZmWkRGhraUF1dzeHz+a2WlpaqzMxM09zcXDPgzqop6uLpjTj7qsjIyCqxWNyYkJBgGxoa2pCRkWFRU1PDuXXrFvfChQuDgoODNY7KhoeH12dkZFhcuXLFGADy8vJ4crm8ffoLn89vLSoq6tCXFhYWyhs3bnDr6uqYq1evmnY3bm31jBo1qmHv3r1Fq1atcm1sbGQ0nQ+ayuszzu5SKpW4ePGiuYuLS4uu/Txy5MiGjIwMi9raWk5WVpZpWVlZp+dzT7X3oZkL1Z8IBAIcPnwYs2fPxsSJE2FlZWXokPqMX375BYcOHUJ2dnaPr7Shq/z8fDz99FIoWeDAoS8Qu+VN7N2zF+XlZfDz84Orq2uH8o6OjlixYgUKCwvB5XLh6emJkpISSKVSwzSgF+zYsQM+Pj5Yv369QfYfFhaGjz76yCD77m+eWbf2RtSSp60XRK3QeiNhQ30d/v3JATbx0L90uvP9ypUrvLVr17oAgFKpZN56661ifSbQutavqfyPP/44SN12pVKJiIiI4bW1tVyVSsVs2rSpWF+xtxEIBM0vvvjisKtXr5pGRkZWiMXiFhcXF8XBgwft3d3dvd3d3ZukUmkDcOeBT+ri0bT9YbFx48aydevWuaxbt+6PlStX3gwMDPQCgJdffrlUIBC0anqfQCBo3bVrl3zmzJkipVIJMzMzVVJS0jUArQDwwgsv3Fi5cuXwrVu3Oh0/fjzf1dVV8eyzz5ZPnDjR09/fv37o0KGdnstNTU0cBwcHv7Z/r1+/vuyVV16p6KyeiRMn1o8ZM6Z2/fr1gvfee69E3fmgqfy+ffuKdY1TX9rmQCuVSmb06NE1CxcurOZwONCln0UiUcvixYsr/Pz8vEQiUZNAIGg2NTVlZTIZb/bs2R7V1dXcpqYmjkQisdi0aVPJwoULq3uqvUw/mmvbpUDbHq/b2qrx90Kjex/p3Bu07XPp0qVwcHDAjh07ei2evqypqQlSqRRxcXGYNWuW3up90OOuVCoRFxeHrdu24dkXXsXS6HXgcDhIS/kREU/MAAC88MIL7Stw3KuwsBCTJ09GU1MTLl26NGA/MMnlcgQEBCA7OxvDhnW80bq3fveuXbsGNzc3qFQqtY9x14WO15q+9WkPQHZ2dqG/v/8tTa+rVCosWLhweMmNP2z+ti9R7VJ2DfV1eGlNpErgYFf1xeefX+uNJxLGx8fbFRYWmuzcubO0x3fWA7oav0wm482cOVOUn59/ubdi68v6+3EfqAxxXCorKzl8Pl9VVlZmFBgY6FVcXHyxp6492dnZg/39/V3VvUZTOPqwrVu3IiEhAaWldL0AgM8++wxisVivyfODys/Px9ixYfjiy6/w9fFULF/9f+BwOKitqcHLz60BACxf9Qx+OXkaY8eGIT8//7466uvrcf36dbz33nsDNnkGgLfeegvr1q27L3nuTW3fAly7ds1gMfQXHA4HiZ98UihwsKtaMCVU9fG+99ib5aVQKBS4WV6Kj/e9xy6YEqoSONhVJX7ySSE9zpsQ0htWr17t7OnpKQ0LC/OMjY29bqhrT7+dwhEQEICKigp4eHh02D6QFrh3cnJCVFQU4uLi8O677xo6HINiWRbx8fHtj8DuK8aNH4+oZaux5tkXOowub35jA0qK5fB9ZAQ2vrMDHA4H+3bvxLjx41F8/Xp7OYVCgTVr1sDa2hp/+ctfDNGEXnH9+nV88803uHLlikHjaJv2k5aWBjc3tweqq+1ac/fTJAsKCmBvb4+srKwHqruvMDExYb/4/PNrqamp5rvf3zP0yam7rOvqajkWFpaqCRMnVn+a+El5WFhYrz5QIigoqMHNza3bd94bWlfjF4vFLTT6/D/9/bgPVIY4Lp9//nlRb+5Pk36bQFdUVKCurs7QYfS4mJgYjBgxAq+99lqvPWiiLzp16hTq6uowZcoUQ4fSgUjkCZFY0iF5/j3nEj499E/weDzs2vOP9jW9PTzF8PQUt5djWRbLly+Hh4cHbt++3eux96Zdu3Zh2bJl4PP5hg4FwJ2l7J5++mm91zsQr0kcDgfjxo1rGDdu3FVDxwIAo0aNMvhyXA+iv8dvKNRvfdPDfFz6bQLdNvJ879zFvnZj2YNycXFBSEgIfvrppwE9QtmZ+Ph4/N///Z/aOcSGNH3aVKT8cByTp/1vWomX1AcffvoVystKIfb63w2BKT8cx/RpU9v/ffLkSRw6dAhGRkZobW0Fj8fDmjVrBuRydkeOHMG3335r6DDa6XMt6LuvQXePRhNCCBm4+lY2QtQKCwt7qB8/rFAo8O233yIqKsrQodxnxowZOPHT9/c9+GbilBmIfHpl+79ZlsWJn77H9OnT27c99thjYFkWCoUCLMuipaVlQCbPcrkc9fX18PLyMnQoAIChQ4fSHOhuaG1txVtvv+3gMNTxkbc3bXLozo3ahBAyUFAC3Q+MHTv2oU6gL1++DKFQCBsbG0OHch8vLy8wAPJluVrL5eX+Di6H02eSyN6UlpaGsWPHGvTbofr6ehw+fBjvvfceysvvrLbm5eWFX3/9FefOndP4PplMBoFAAFNTU5iZmcHY2PihfMjRxYsXTYJCQr2+PPpfp+i3d3H//c23TsGhj3pdvHjR4E9mI4QQQ+i3UzgeJkFBQcjPz0dVVVWfTCJ7WkZGRq8/sa6rGIbBtGlTkfLjcXhKNCfHP/9wDNOmTR1wU4y6oi2BNhSnYUKUlVy/b3tu7p0PPXV1dR2Oi6PAGaXFcgCAhYUFeDweKisrwePxMGjQoIfqGLa2tmLzli0O78XFOz2x8nlm6lPLGQ6HA5+QxzjHP/2H2WNjw6Trn3+u9I3XX7/B5er0FFxCCOnXaAS6H+DxeAgJCcHJkycNHYpBZGRkICgoyNBhaDR9+nSk/vS91jLJ3xx+aOfHpqamIiwszGD7Lyu5jhFvfw9TeyHuXY6ZwzODx+IdCNz8c/vPvcl2a2srGhsb8f3338Pc3Bx+fn7QRVxcHBiGucQwzGWGYZ5/0Pb0lrtHnbd8/B/O9IiVTNs9CFwuFzMio5ktH/+H88XXyU5BIaHdHo2urKzkDBkyxO/NN9906Er5+Ph4u5iYGKfOypWXl3N9fHy8xGKxVCKRSA8dOqR19KGz8rrG+aDx95bExETr1157bWh335+dnW0ikUik5ubmAWlpaeadv6N7utpv8fHxdsbGxiPKysqMAGDcuHEeycnJlj0VV1dcuHDBJDAwUOzu7u7t5eUlzc/P5wGAubl5QHfrFIlE3jKZjNdZ/z/o8e1MV49LcnKypaWl5SMSiUQqEom8N2zYoDWm7p5XPd3eu9EIdD8RFBSErKwszJgxw9Ch9Lrz589j6dKlhg5DowkTJiB61So422rOH8zMzR/KVVQaGhpQVFQEHx8fg8bBcI3g9uTb+P2DNWBbm/+3ncOBlXugxvcJBAK8+OKLEAqFaG1tha+vLyZPnvznEnYMwDXG3/++C88//yzUjcBeunQJBw4cAIAQAC0AjjMMk8yybIG+26hvEydNlkxauJT7+NPrGE037zq6uOGtfxzh/OfjvWYTJ02W3Cgvy9Z1Pxs3bnT08fHR+1J4fD5fdfLkSZm1tbWqrKzMyM/PT7po0aIqTSPlnZXvqTgNLSIiohqATo9hv5u/v39zbm5uTkhIiLjz0r3D1NRU9c9//tN248aNNw0dCwBERkYO/+tf/1o8ZcqUusLCQmNTU1OVvururP8f9PjqU1BQUF1KSkqBQqGASCTyWb58+W1PT0+1S+B197zqzfbSCLQe5OTkIC4uDkVFPbc0obW1Nerr63us/r7sjz/+gIPDAw36aFVVVYXS0lLU1tZ26/0WFha4UV4OlmU1/kyeNAlNTU16jrzvq6+vh4WFhdrkUh9UKhWSkpKQmJgIlUr73ySzIa5wHLcYHGPTOxs4XNgFTAXD0RxbZWUlDh8+jNzcXAwaNAiDBw/G3/72NwQ/OhpGdq6wmfQyNsf/EyEjH1P7kJzff/8doaGhYFm2gWXZVgCpAPrFcjourq4tAjdPjclzGy6XC8FwEeM6fHiz1oJqZGdnm1RUVBg/8sgjek9MTUxMWGtraxUA3L59m9vS0sJRKBRMTEyMU3BwsFgoFPpERUUJXV1dfUpLS400ldcW5+bNm4e4ubl5e3p6StesWSPQdxuSk5MtR40aJZo6daqbSCTyXrZsmTMAJCUlWfv6+nqJxWLpihUr2p9MJJfLjcaPH+8hFoul3t7eXhcuXDDRVn7u3Lmujo6OvosXL1h0w20AACAASURBVBa2bdu3b5/t3f9etmyZ8/vvv28HAG11+/j4eG3bts1e3+3VlwkTJlQfOXLkvjUzNcWva79pOi7qnD592ozH47FTpkypAwBXV1fF0KFD2x9YsWTJEmdXV1efJ5980qWzOF9//XUHNzc372nTprk1Nzd3OpdM3fHtC2prazkKhYJp+yChqZ81eeONNxyGDx/uPX78eA9nZ2cfmUzGAzS39+6R/pCQEHFaWpq5puuALu2gBPoB1dXVITQ0FC+//DJCQkLuW41BX8zNzdHQMOAGP7qksbERZmZmPVJ3RkYGLl68iIKCAkRERPTIPgDAzMwMjY0P33KZDQ0NMDfvsW918f7772PlypWIjo7u0gomQx9bAJPBzgDDAcM1wuAg7d/oJCcn4+TJk5gxYwakUilsbPh49fW3cJUnhe2cHTAZ5g+Tia/jipEEjwSG4HpxSYdrgI+PD3755RcwDGPHMIw5gOkANP6x7UumTJp4+7dff+rSSFnWrz+ppkyaWKnrPl588cVhW7du7bFHrVZWVnI8PT2lQUFB0r/+9a9FpqamLABMnz69asKECdVubm7N4eHh1SkpKYO0ldcUZ2xsrNO5c+d+z8vLy3n99ddv9EQbsrKyLDZv3lyan59/OTY2trSkpMRo+/btTqdOnZLJZLKckpIS3tGjRy0BYNWqVcIpU6ZUy2SynF9++UVmZ2en1Fb+yJEjha+++mqHdi1YsKAqJSXFqu0D6c8//2z95JNPVgHAgQMHimQyWc65c+dyExISHORyeZ/8FtvKyko5dOhQxfnz503v3q4pfl37Dbj/uGiKJT8/38TV1VXth8vGxkZOZGTkbZlMdjk1NdWqsLDQWFOcMpmMd+jQIfsLFy7kbN68ubS4uLjTKVPqjq8hZWZmWkgkEumwYcP858+f/4dQKGztrJ/vlZ+fz/v444/ts7Ozf//73/9eXFJS0t4PurZX03Wgq/rkyd+fKBQKKBQKtLS0oL6+HizL9shNRhwOp9MRtoGqp/oUAGpqatr30ZMPM2EYpsc+XPVlPXnsgDsjxEqlEizLorKy8/yN4XDh/uSbuBS3FByeGVrrq6FSNINjrP5vEZfLBcuyuHDhAhiGAddiMKxmbYOxjaBDnabeM2A0bARKTsSh4o/LyM/Ph0gkgpeXFzZs2IAVK1Z8D6AewG8A+sXjUufMmVOdMOtxp86OIcuy+O3kz9j+6nqdvjZNSkqy9vDwaNL0Fa4+8Pl8VV5eXs758+dNV65c6bJkyZIqALC1tW2tqanhtv23qqqKq6n84cOHrTTF6evrW79o0SLXadOmVUdERFT1RBukUmlDcHBwEwAMHjxYmZiYaF1cXMwLDg6WAEBDQwOnoKDABEDtmTNnLI8cOXLtzzaqAKi0lVe3P1tbW5WHh0dTamqqOY/HY4cNG9Y8ePBgJQDs2bPH/tixYzYsy+LmzZvGcrmcJxQK++R6hlFRUX8cPHjQ7u5tmuLvTr/de1w0xaHtum9sbMxOmjSpHgCGDRvWUlxcbOzq6qpQF+eVK1d4wcHBdRYWFmxQUFCTo6Njv3sqY9sUjsrKSs7YsWPF6enplQUFBTxdzs/Tp0+bh4SE1FlZWakCAgIeqB80XQe6ihLoB8Tn83HkyBF89tlnWL16dY896KOhoQGDBun04WjA6MnR2/Hjx8PNzQ319fVISkrqkX0APTuK3peZm5v36Mj7Sy+9hMbGRqhUKrzyyitdeo+JrQDDJkej+Phe5P1zvdoyQUFBGDt2LFpbW2FsbIyWlhawLIvWxhrUnIiH9bjnYGTd8b4ZI2tHWM/aioYL32DUmDBUlN8ZCFm+fDmWL18eCAAMw2wDUPwATe41AQEBTQygKr6az3F299RYrvhKHrgMowoICNBpjtKZM2cGffvtt/zhw4fbVFVVGTEMA0dHR8WaNWv0/kl2xIgRTcbGxmx6eroZcOcD7d0/SqWS0VReW5xpaWn53333nUVSUpLt/v37h1y+fPl3fcduZWV1X3I2ZsyYmqNHj3Z5MXNdy8+dO7fyyy+/5PN4PHbOnDmVwJ1pCykpKVZnz57NtbS0VPn4+HjdPajDMEyfGiF44oknqjdv3uxkZ2enADqPXx1t/abuuKgjEomaCwsL1X5CNzIyau8zhmGgUqk0xtnZQERf639t+Hy+asyYMTUpKSkWzs7OLdr6WR/turvv7ty/8r/t2q4DnaEpHHowbdo0fPzxxxg5cmSP7aOuru6hTMAAwMrKqsdGhxmGgUAggKenJ4TCnpsmVllZCSsrqx6rv68yNzdHXV1dj05t2rFjB2JjY3WaKuIwel77qhsBb/wXnkv/DsdxS2Dpdmeq3Llz57Br1y7s3r0bCoXif29sbUbrrWv446uXUP/bkfvqZThccPnOcHMXtW+7efPOfUwMwwhxZ/5zz31S0yMOh4Pw8PDq307+rLVc1smfER4eXqXr4EF8fHypXC6/dO3atctLly6teOaZZ8r1mTxfu3bNuLy8nAvcmeNaUFBg5uLiotC1vKY4VSoVCgoKeDNmzKjbuXNnSWlpKU9fsWsTHh5en5GRYXHlyhVjAMjLy+O1TUUYOXJk7e7du+0AoKamhlNaWmqkrbwmC/+/vTsPa+rMFwf+zUoICRh2iEAEsxACkSUotChaHVyoS2mLBcSOitXe1pmLc+vU1rHWpb12RivWmaq0U39W5jptpeOlo07nimWqFkEQUSRsQgybIhBAtiQnvz9sGNScQDAQ0O/neXh8TF5Ovu+S8M173nPexMT277//3un06dOTXnnllXYAAI1GQ+XxeDoul0sUFhayysvLH/iDxOPxdHV1dWPSBsNBp9NhxowZnUVFRRwA8/Fbq91MiY6O7unu7qaeOXOGAwBQV1c3MM5MIYtzxowZ3YWFhZzu7m7K5cuXWY2NjQ+09Xhrf3P0ej2Ulpay/fz8+odq54frFRUV1V1QUMDp7OykFhcXP9IOpnA4HH1zczOtq6uLUlNTwxqq/HBhAj1BlJSUgFQqHbrgE2jatGlQXFxs6zBGjCAIuHLlCkybNs3WoYw5BwcHcHFxgaqq8XvTCSqTBVz/UPCesxJEv/wDAMDAxZ/r169/NPmn0gAMBPQ3lJo8nrauAF5a9u+t3RMSEoBCoZQBwP8CwH8YDIZROdU/GuIXLmgvOf9/ZmfaSs6f1ccvWjju6lRdXc2cOXOmWCQSSWfPni3eunWr2lwCbWl5g8EAycnJU0QikfSZZ56RbNu2bUzOLPD5fN3evXtV8fHxQpFIJH3ppZf8Ozs7aQAAn376qerUqVOTxGKxNDo6WtzS0kIjK69UKpkSiUT6wQcfeP/v//4vTyKRSI8fP+4EAODh4aF3c3PTcrlcPZ/P1wEAJCQkdBgMBggICAjavHkzXyqVPnBRzsaNG5u3bNkyWSKRSI1reW1t9erVd7u6umgA5uO3pN1GEsfRo0dvvv3225P9/f2D5s2bJzR3HLI4RSJRf0pKyh2ZTCbdvHkzn8/nP7Cu+uH2N9e/tmJcAy2RSIICAgJ6ExMTNUO188P1EgqF/ampqXdCQkICN23axOfz+X0sFstgrr4bNmxomjt3rmjdunU+np6epO9pS1Em0LrMBwI13lP33LlzDxQyTtWPpF5kxxxNw3lNvV4Prq6ucOPGDfD0HJPbG44r+/fvh2vXrsHBgwdH5fij3e9KpRLmz5//1G4fnZKSArGxsbBmzRrSMqPZBxQKBcK3m59FHezyljkDnx+xsbHwww8/AIPBAK1WC1SuB9iL54B9wDNA4z56ZxiDwQCtf0mDkoILD3/hHXe7r5SUlNTK5fIWc2U0Gg3VPyAgpPXuXdI/+M4uLvqa6uqrxjtYjLaMjAyX2tpauz179oybi6MsMdHjtxVst/HJFv3S1tZG5fF4RGNjIz08PDxQrVaXjtby2ZKSEle5XC4w9RyugZ4ASktLwcPD46lMngEAFAoF/PnPf7Z1GCM2nndSHAvGrejNJdDjlb29Pfj6+sKrr74K+//4KVBnbQI6j/wmGvp2NVAM+idmy3YnJyfibkvLFVvHgRBCRuvWrfMpLi52AADYvXv3rdFKnoeCCfQEYOutkG1t2rRpUF5ePmEvxHvaE+hZs2bBrl27bB3GiHz33XcDFwbXqRvgxPUrZhPovltF4MLjPVXbfY+1iIiIbn9//wl3BwKjiR6/rWC7jU+26Jfjx4+P3qYbFsAEegL44YcfYNmyZbYOw2ZYLBY8++yz8M0330BKSoqtw7GIVquFr7/+Gk6fPm3rUGxGJBJBb28v1NXVgZ+f39C/YGVefB+4vGWOReWNBs9sLFscDzk/bAOA50381n26ukvg4vr0XSw6lqKjoyf0DdUnevy2gu02Pj3N/YIJ9Dh39+5dyM3NhQMHDtg6FJt68803YceOHRMugT5x4gSIRCIIDg62dSg2Q6FQYOHChZCVlQVvv/32mL9+g1pF+pwla6+fe+45INrToDnzJdIydCYLeMIIS0NECCE0wUzYBLqqqgq6uroG/gA+qQ4cOAAvvPDCU7v+2WjhwoXw61//GvLz82H69Om2DmfYMjIy4De/+Y2tw7C59PR0mDdvHvz617+ekMtwAO5v2d7acnvg/8ZlGrNmzRp47MqVK0/txaIIIfQ0mbC3sXNzcwMOh2PrMEZVR0cHfPLJJ/Bf//Vftg7F5mg0GrzxxhuQkZFh61CGrbCwEOrr6+H558lP+T8tZDIZREREQGZmpq1DGVUcDgfc3NxsHQZCCKFRNmET6OLiYlCr1XDu3LkHfmg0GtBoI7pV47iza9cuWLRoEYjFYluHMi788pe/hH/+859QUFBg61CGRBAEbNq0Cf7zP/8T6PQJe6LHqrZv3w47duyA9vZxd8vgETF+1gz+/FGr1RP6nuVjjUajhf98X1jpq6++Sn515iAZGRku6enp3kOVa2pqoslkskCxWCyVSCTSo0ePThpp+ZHE+bjxP0woFAYplcohN404duyY0+bNmx84Zclms0NNlTX1eElJiZ1EIpGy2ezQvLy84e9ONMqG224ZGRkuDAYjrLGxkQ4AMHv27Kk5OTnc0Y+Q3NWrV+3Cw8PFAQEBQYGBgdLKykomAHm/DIdxPAzVX6bGgzUNt19ycnK4XC53mkQikQqFwqBNmzaZjWmk43C06zsY/mUfp2pqauDw4cNQWmp6s4an0aRJk2D//v2wYsUKKCoqsmjnubG2f/9+6O3thTfeeMPWoYwb06ZNg+effx7ef/992LNnj63DQeOAnZ0dUV5eXjYax+bxeMT58+eVTk5ORGNjIz0kJESalJTUTjbBYq78aMZpbcnJyRoA0Iz09+VyeV95eXlZZGTkhJ25YbFYxOeff+78zjvv3B669OhLSUmZ8tFHH6nj4uK6amtrGSwWy2r3TB+qvx53PFhTREREV25ubpVWqwWhUChbvXp1q0gkMnkHj5GOw7Gs74SdgX6SdXZ2wgsvvABbtmwBb2+LJyqeaC+//DKEh4fDb3/7W1uHQurGjRuwY8cOOHLkyBNzNsRadu7cCX/961/hxIkTtg4FPeHs7OwMxs1dWltbaf39/VStVktJT0/3VigUYl9fX9mKFSt8BQKBrKGhgU5W3txrbN++3d3f3z9IJBJJ169fz7d2Hd59910Pf3//oAULFvj39fUNxJKTk8ONjo4Wzp8/318oFAatWrXKBwBg2bJlAi8vr+DU1FTfwccxGAywfPlyP6FQGPTLX/7SZ6jHyWRlZTkFBwcHisVi6Zo1ayZbs67W9Nxzz2mys7N5Dz8+Z86cqWKxWCqTyQJ37do1sNZKpVLRjc8FBQUFXr161Q6AvL5k7W/KxYsX7ZlMpiEuLq4LAEAgEGg9PT0HdvdcuXKlj0AgkC1fvnzgFkVkcZKNBzJk48HWOjs7qVqtlmL8ImHpuNqyZYvHlClTgubMmTPVx8dHZjwzQ1bfwTP9kZGR4ry8PDbZ54Al9cAEepzR6XSQmJgI06dPh1/96le2Dmdc+uSTT+Dbb7+F77//3tahPKK/vx9WrFgBO3bsgKlTp9o6nHHHw8MD/va3v8Frr70Gly5dsnU4aBAKhRJO9vP73//e1Vju97//vau5spa8Zn9/P1UqlQaGhYVJTp06ZfWLWtra2qgikUgaEREh/eijj+pYLJYBAGDhwoXtzz33nMbf378vNjZWk5ub62CuPFmcu3fv9r58+fKNioqKsnfffbfZmrErlUrm0aNH3a5evVq2ffv2BrVabTf4+eLiYs727dsbKisrr+/evbsBACA7O7v27bfffmRHuN7eXupLL73UVl5efv3q1avs06dPc8w9bkp9fT39gw8+8L5w4YJSqVSW1dfXM0+ePGnTpRFkHB0d9Z6entqioiLW4McPHz5cp1Qqyy5fvlx+6NAhD5VKRQcAeO2113zj4uI0SqWy7F//+pfSxcVFP1R9TbW/KZWVlXYCgaDP1HM9PT3UlJSUVqVSef2HH35wNG6BbirOocaDKWTjwVaMW3lPnjxZ/tJLL9319fXVWTquKisrmUeOHHErKSm58Yc//EFdX18/0A6W1pfsc2C4MIEeRwwGA2zYsAH0ej188sknuBkDCR6PB0eOHIGUlBQoKiqydTgDdDodJCUlgZ+fH6xdu9bW4Yxb4eHh8Nlnn8HSpUuhtrbW1uEgG6qpqSkpKyu7sXfvXtWrr77q39XVZdUPPR6PR1RUVJSdP3/+xsGDB92Ns3bOzs66SZMm6Z2dnXU8Hk/f3t5OM1eeLM7g4OB7SUlJgoyMDBdjsm0tP/30E1uhUHRxOBxDREREr5eX1wOnuqVSabdCoegFAHB1ddWbPsp9NBoNli5d2kGj0SA2NrYjPz+fbe5xU86dO+egVquZCoVCIpFIpNevX2dXVVUNmcTZyooVK+5mZma6DH7swIEDboGBgVK5XB54+/ZthkqlYgIA/PTTT9xf/epXLQAAzs7OBJ/P1w1V3+G2v8FAPiwYDIZh3rx59+zs7AyTJ0/uV6vVDLI4hxoPE0FERERXeXl5WX19fcn//d//OeXn59tbOq4uXrzIjoyM7HJ0dCRCQ0Mfqx3IPgeGC9dAjyN79+6FH3/8EX788UdgMBi2Dmdcmz17Nnz66aewcOFCOHXqFISGjvhaDKvQarWwcuVKuHfvHnz77bf45WcIixcvhtraWli0aBGcP3/e1uEgADAYDJeHU+43v/lNy29+85sWa7ymr6+vDgBg1qxZ3e7u7tqKigq7sLCwXmsce7CwsLBeBoNhyM/Ptwe4fwvCwT96vZ5CVn7mzJndZHHm5eVVnjlzhpOVleV88OBB9+vXr9+wVsxDfYY4OjqaTZpHcuzBj1MolEcyv5iYmI6TJ09OiPs0JiQkaLZv3+7t4uKiBbi/7CI3N9fx0qVL5Vwul5DJZIEEYX4psrn6Drf9hUJhX21trcmEkE6nD7QxhUIBgiBI4xxqPJjqr/GKx+MRMTExHbm5uRwfH59+c+1sjXoNbju9Xv/A4+Y+B4aCM9DjgMFggHfffRf27dsHOTk54OiIO5kNx7Jly+CPf/wjxMXFwdmzZ20Wx71792DJkiXQ0dEBJ06cADu7cTspM65s2LAB5s+fDzExMdDT89RuZvXUam5uphlncpVKJbO5uZkxdepUq82q3bx5k9HU1EQDuL/Gtaqqyt7Pz09raXmyOAmCgKqqKuaiRYu69uzZU9/Q0DDkHTIsMWPGjO7CwkJOd3c35fLly6zGxsYRH1+v18PJkycd9Xo9nDt3znHGjBn3zD0OAMDj8XR1dXUDrxkbG3uvoKCAU11dzQAAqKioYBqXQIxHdDodZsyY0VlUVMQBANBoNFQej6fjcrlEYWEhq7y8fOCG9FFRUZ379+93AQDo6OigNjQ00K1V3+jo6J7u7m7qmTNnOAAAdXV1A+PMFLI4hxoPD/fXeKbX66G0tJTt5+fXP1Q7P1yvqKio7oKCAk5nZye1uLh4WO8LDoejN76Pa2pqWEOVH65xO/ifJtevXwcqlQoFBQXg7u5u63AmlBdeeAGcnZ3h5ZdfhjfffBM2bdoETObYfYZcunQJVq9eDQqFAg4ePIhnDiz0+9//Hg4ePAgbNmx4qndrfBpdvXqVlZaWNoXJZBI0Gg0++eSTWkdHR6vdnaC6upr5+uuv+wEA6PV6ytatW9XmEmiy8t9//72DqTj1ej0kJydP6ezspBEEQdm2bZvaWrEDAIhEov6UlJQ7MplMKhQKe/l8vsl1tEZKpZK5ZMmSqRqNhtbb20uVSCScbdu21ScmJmpYLBaRlZXl/NZbb/k8++yzHb/4xS/uAdy/W4WpxwEANm7c2JyWljZl586d3qdPn64UCATavXv3quLj44V6vR7s7e2JrKysmwCgs2a9rWn16tV3P/vsMw8AgISEhI7MzEy3gICAoICAgF6pVNptLPfpp5+qXn31VUFmZqY7g8EwZGVl1YSEhPRZq75Hjx69mZaWJli/fj2NTqcbvvvuu6rBFxIORhbnUOPh4f7q6+ujkI0HS+O3FuMaaL1eT3nmmWc6EhMTNVQqFcy188P1EgqF/ampqXdCQkICje3AYrEM5sb/hg0bmubOnSuSy+X3PD09ST8DLEUxtz5nnBlWoMZ77up0lr+nLdnW11piY2Oho6MDLl26hPcLfgwqlQrWr18Pt27dgszMTIiMjBz2746k3+/duwdbtmyBrKws2Lt3LyxfvhyXbTyG8PBwcHBwgLy8vDF93cd5z1v4WTPuBkdJSUmtXC63ylKMsZSRkeFSW1trt2fPnnFzcZQlJnr8toLtNj7Zol/a2tqoPB6PaGxspIeHhweq1epSKnV0FlSUlJS4yuVyganncAnHOODo6IjJ82Py9fWFnJwcePvtt2Hx4sWQnp4OTU1NVn8dvV4P3377LQQHB8OdO3fg2rVr8Morr2Dy/Ji4XC6M1gcgQgihJ8e6det8RCKRdNasWaLdu3ffstXfDsza0BODQqHAK6+8AvPmzYMtW7ZAYGAgPPfcc7B27VqYO3fuYyVoarUaPv/8c8jMzAQvLy/44x//CPPnz7di9Aih4YiIiOj29/efcHcgMJro8dsKttv4ZIt+OX78eN1Yvh4ZTKDRE8fV1RX+9Kc/wX//939DVlYWbNq0CTQaDSQkJEBYWBiEhYWBUCg0m1C3trZCcXExFBcXw7lz5+DChQuwfPlyOHnyJEybNm0Ma4MQGiw6OnpCX3E60eO3FWy38elp7pcnLoEmCAIMBsPA2kZLXLlyBTgcq9/LH9mIo6MjrFu3Dl577TUoLCyEf/zjH/D111/DO++8A3fu3IGQkBDw8PCAGzduAIVCgZdffhm6urqgrKwMWltbQS6XQ1hYGLzyyivwP//zPzg2nkBVVVXQ1dU1os8LvV6PS3cQQugp9cQl0I/zB43D4YCbm9vQBdGEQqFQQKFQgEKhGHisra0NSkpK4O7du3Djxg0wGAzw4osvgr29PYjFYpg6dSquyX0KPM773Xjv0KdFR0cH9d0tv/M6lpXllpKcfHvH9vebuFyu1e6agRBCE8kTl0Bzufd3gBzLO2mgiYfH4w3MOu7fvx8AAF5++WUbRoRsobi4eMS/O2nSJCtGMn4RBAGHDx92fnfre758aThl6TufUM/97YiHv1DkvmPbe6q0tLRW/LKJEHraPHEJNEIIIevIy8tj/8eGXws03X128f/1B6pf8P2zOD7SUGpdaQHs+MNOvwOfHvI8kPFxbUxMTPcQh0MIoScGThsghBB6RFlZGXP+wkUS/zkv2r+675uB5NnIL1gBv9z3DXXK7AT7uAULJWVlZRbvYHT27FkHkUgk9ff3D1q4cKH/cH4nIyPDJT093Xuock1NTTSZTBYoFoulEolEevToUbOnDMyVH0mcjxs/etBw2y0jI8OFwWCENTY20gEAZs+ePTUnJ4c7+hGSu3r1ql14eLg4ICAgKDAwUFpZWckEAGCz2aEjPaZQKAxSKpXMkpISO4lEImWz2aF5eXnsh8sdO3bMafPmzZ6PE785w+2XnJwcLpfLnSaRSKRCoTBo06ZNZmMaql5kRru+g+EMNEIIoUe0trbSXb19idD5L5FuO0yl0SB0/ktQnHOMaG1tpQPAsG9npdfrYdWqVVMOHjxYGxcX19XQ0GDVv0c8Ho84f/680snJiWhsbKSHhIRIk5KS2mk009UhKw8Aoxonsj4Wi0V8/vnnzu+8885tW8cCAJCSkjLlo48+UsfFxXXV1tYyWCyW1a4dkMvlfeXl5WWRkZFiU88nJydrAMBmuw8OFhER0ZWbm1ul1WpBKBTKVq9e3SoSiUx+ZgxVLzJjWV+cgUYIITTmfvzxR7azs7MuLi6uCwDA29vbqltC29nZGZycnAgAgNbWVlp/fz9Vq9VS0tPTvRUKhdjX11e2YsUKX4FAIGtoaKCTlTcX5/bt2939/f2DRCKRdP369Xxrxg8AoFKp6HPmzJkqFoulQUFBgVevXrUDuD+bFx0dLZw/f76/UCgMWrVqlQ8AwI4dO9yFQmGQUCgM2rdvn8tQcY52/Lby3HPPabKzs3kPP25sS5lMFrhr166BK4jJ2jkrK8spODg4UCwWS9esWTPZWJ6s/U25ePGiPZPJNBjHj0Ag0A7exnvlypU+AoFAtnz5cr+h4nz33Xc9/P39gxYsWODf19c35BXMy5YtE3h5eQWnpqb6DlV2LHV2dlK1Wi3F+EWCrJ3JbNmyxWPKlClBc+bMmerj4yNTKpVMAPL6Dp7pj4yMFOfl5bHJPgcsqQd+k0YIITTmbt68yeRyubqYmBhhS0sLY+XKlXd++9vf3rHma7S1tVGnT58uuXXrlt2+fftqWSyWAQBg4cKF7SqViunr69tPo9E0ubm5DsnJyRpT5c3FuXv3bm+VSnWVx+MR9fX1Vv97+tprr/nGxcVpNm3adKe1tZXa09MzMOlVXFzMycvLu6FQKHpbb3MCaAAAIABJREFUWlpoSqWSeejQIffS0tKy/v5+SkhIiPTFF1/U8Pl8HVmcox2/rTg6Ouo9PT21RUVFrMGPHz58uC4gIEDb19dHEYvFQSkpKW2+vr46U+1cX19P/+CDD7wvXLig5HK5xIIFC/xPnjzJXbx4cSfAo+1PFktlZaWdQCDoM/VcT08PNSUlpfXQoUNqgUAgq62tZQgEAq2pOHt6eqhHjx51u379+vXy8nK7yMjIoKHaITs7uzYjI8OlsLDQwdI2HA2FhYUciUQiValUdmvXrm329fXVDdXOD6usrGQeOXLE7dq1a2XV1dXM8PDwgXawtL5knwPDrc8T84ZBCCE0cfT09FCLioo4RUVFZS4uLrqwsDDp4sWLNVKp1Gq7mvF4PKKioqKsqKiIlZaW5rdy5cp2AABnZ2ddR0cHzfhve3s7jay8uTiDg4PvJSUlCRYsWKBJTk5ut1bcRj/99BM3Ozv75s8xEwAwcOpfKpV2KxSKXgAAV1dX/d///nduZGRkl3EWXS6X3ysoKLDn8/mdZHGOdvy2tGLFiruZmZkugx87cOCA26lTpyYZDAa4ffs24+fkSWeqnY8dO+akVquZCoVCAgDQ3d1NraqqsgOAToBH258sDoPBQBojg8EwzJs37x4AwOTJk/vVajVDIBBoTcVZXV3NVCgUXRwOxxAREdHr5eU14XZlNC7haGtro86cOVOcn5/fVlVVxTTXzg+7ePEiOzIyssvR0ZEIDQ19rHYg+xwYLlzCgRBCaMx5eXlpAwICeoVCYb+zszMRHBx8r7S01H40XissLKyXwWAY8vPz7QH+fQ9v449er6eQlTcXZ15eXuUbb7xxOz8/32HmzJmi0YidjKOjI2nS9jCyOG0Z/2hLSEjQ5OXlORLE/e8cOTk53NzcXMdLly6Vl5eXl/n7+/canyMTExPTUV5eXlZeXl6mUqmupaentxifG277C4XCvtraWjtTz9Hp9IHsmkKhAEEQpHEOdc95CoVCnqmPMzwej4iJienIzc3lAJhvZ2vUa3Db6fX6Bx439zkwFEygEUIIPcLZ2VnX0qCiFp36KxB607kCoddD0am/QkuDiurs7GzRGuaYmJjuhoYGZnNzM623t5dSXl7OFolEJk91j8TNmzcZTU1NNID7a1yrqqrs/fz8tJaWJ4uTIAioqqpiLlq0qGvPnj31DQ0NFt+FZChRUVGd+/fvdwG4v5GNuTWa06dP7y4oKOB0dHRQW1paaFevXnVQKBQ9ZHGORfy2RKfTYcaMGZ1FRUUcAACNRkPl8Xg6LpdLFBYWssrLywe+rJlq59jY2HsFBQWc6upqBgBARUUFU6VSWXzWPjo6uqe7u5t65swZDgBAXV3dwDgzhSzOGTNmdBcWFnK6u7sply9fZjU2Nj7QXzweT1dXVzch+lCv10NpaSnbz8+vf6h2frheUVFR3QUFBZzOzk5qcXHxI+1gCofD0Tc3N9O6urooNTU1rKHKDxcu4UAIIfQIqVTaf/rv35W/seHXguLvsuzmvvbOA7eyqystgH8e3EnwHFh9Z079vdbSpRcuLi76Dz/88NasWbPEOp2OkpCQcDc0NLTXWvFXV1czX3/9dT8AAL1eT9m6davaXAJtrrypOPV6PSQnJ0/p7OykEQRB2bZtm9pasRt9+umnqldffVWQmZnpzmAwDFlZWTVkF1uKxeL+tLS02+Hh4YEAAG+99VYDn8/XkcVpMBhGPX5bW7169d3PPvvMAwAgISGhIzMz0y0gICAoICCgVyqVDty33FQ7h4SE9O3du1cVHx8v1Ov1YG9vT2RlZd0EAIsvdj169OjNtLQ0wfr162l0Ot3w3XffVQ2+kHAwsjhFIlF/SkrKHZlMJhUKhb18Pv+BL5sbN25sTktLm7Jz507v06dPV/b19VGWLFkyVaPR0Hp7e6kSiYSzbdu2+sTERJvdkcO4Blqv11OeeeaZjsTERA2VSgVz7fxwvYRCYX9qauqdkJCQQGM7sFgsg1KpZJLVd8OGDU1z584VyeXye56enqSfAZaimFufM84MK1Dj7mDt7RNjOZdxNzzcOdF2sA9sbyL2gYWfNeNuz++SkpJauVzeMlQ5giAgMzOT987vtvp6B4ZRIxanUgv/doRoKC8mdr6/TbVmzZq2sdyJMCMjw6W2ttZuz549DWP2olY00eO3FWy38ckW/dLW1kbl8XhEY2MjPTw8PFCtVpeO1mdQSUmJq1wuF5h6DpdwIIQQIkWlUmHt2rVtNZUVpbFyUfO3u97Uzw6VNNdUVpSuXbt2TJNnhBBat26dj0gkks6aNUu0e/fuW7b6DMIlHAghhIbE5XKJfR/vbdj38V6bzgBGRER0+/v7T7g7EBhN9PhtBdttfLJFvxw/frxuLF+PDCbQCCGEJozo6OgeW8fwOCZ6/LaC7TY+Pc39gufeEEIIIYQQsgAm0AghhIak0+ngd1vf83Bx95y29b1tHjqdVXfeRgihCQUTaIQQQmaVlpbaTYuIDMw8/jfvoJW/ox3+n2+9QxXTA0tLS01uEIEQQk86TKARQgiZZJx1jnp2ppQZNMc++q3DVI+gGRD91iEqPTDWPurZmdKt723z0JNstGLON9984yiRSKTGHwaDEXbhwoUhdyLMyMhwSU9P9x6qXFNTE00mkwWKxWKpRCKRHj16dNJIy589e9ZBJBJJ/f39gxYuXOg/vBo+Xvxj5dixY06bN2/2HOnvl5SU2EkkEimbzQ7Ny8tjWzO2wYbbbsuWLRNIJBKpq6ur3N3dPUQikUiTkpJ8AQDYbHboaMVnqYSEBIG3t3cwwP33GY/Hk6empvpa+3Uet3+HMtx+ycnJ4XK53GkSiUQqFAqDNm3aZDamkY6r0a7vYHgRIUIIoUeUlpbavbJipX9rr4H17Nt/pnI9//23nUKlwdRfJFM8QmIoh7/Y5n3ibyeds/7fFzXBwcHD3kkwISGhIyEhoQzg/u5sM2fOFFvzgiQej0ecP39e6eTkRDQ2NtJDQkKkSUlJ7TSa6U3gyMoDAKxatWrKwYMHa+Pi4rrM7QY4ESUnJ2sAYMSba8jl8r7y8vKyyMhIsRXDGrHs7OxaAID09HRvDoejf//995ttHBIpNptNXLx40b69vZ3m6uo6KmuiHrd/rSkiIqIrNze3SqvVglAolK1evbpVJBKZvIPHSMfVWNYXZ6ARQgg9YvZz8yQ04TP20W8dfiB5Hozr6QvRbx2iUqdG289+bp5kpK/1xRdf8OLj49tGHKwJdnZ2BicnJwIAoLW1ldbf30/VarWU9PR0b4VCIfb19ZWtWLHCVyAQyBoaGuhk5X/88Ue2s7OzLi4urgsAYPBOgNu3b3f39/cPEolE0vXr1/OtGT/A/Vm76Oho4fz58/2FQmHQqlWrfAAAsrKynIKDgwPFYrF0zZo1k43lVSoVfc6cOVPFYrE0KCgo8OrVq3bmyi9btkzg5eUVPHjm89NPP3Ue/P9Vq1b5fPLJJy4AAMZjy2SywF27drlZu75jYeXKlT4CgUC2fPlyP+NjO3bscBcKhUFCoTBo3759LgD3E3BT4wSAvB0sHQ9xcXHtJ06cmJSdnT0pLi5uYEcmsv4aPIMeGRkpNs7MstnsUFP1MtW/5o5PNt6srbOzk6rVaiksFoswFw+ZLVu2eEyZMiVozpw5U318fGRKpZIJQF5fU+1mrn+HCxNohBBCj/DxE/RzvP0plCE2KaBQacDxmkLxFUwZ9uzzw7766iuXlJSU1pH+Ppm2tjaqSCSSRkRESD/66KM6FotlAABYuHBh+3PPPafx9/fvi42N1eTm5jqQlb958yaTy+XqYmJihIGBgdIPP/xwIGHavXu39+XLl29UVFSUvfvuu6My01lcXMzZvn17Q2Vl5fXdu3c31NfX0z/44APvCxcuKJVKZVl9fT3z5MmTXACA1157zTcuLk6jVCrL/vWvfyldXFz05spnZ2fXvv322w/c1/vll19uz83NdSQIAgAAzp4967R8+fJ2AIDDhw/XKZXKssuXL5cfOnTIQ6VSTajZ+J6eHmpKSkqrUqm8/sMPPzjW1tYylEol89ChQ+6FhYU3Lly4UP7hhx9619fX0wHIxwlZO1g6HiIiIrpLSkrYVVVVLJFI1AsAYK6/LKkXgOn+Her4D483S9vYHONW3pMnT5a/9NJLd319fXWW1reyspJ55MgRt5KSkht/+MMf1PX19QPXYZiqrzlk/TtcE2rwI4QQGhvx8+e1/vXCeZZ36KwhJ1ruXDtPLJ8/b0QzyCUlJXY9PT3UqKgoq99PlsfjERUVFWVFRUWstLQ0v5UrV7YDADg7O+s6Ojpoxn/b29tpZOV7enqoRUVFnKKiojIXFxddWFiYdPHixRqpVNofHBx8LykpSbBgwQJNcnLysPZ0t5RUKu1WKBS9AACurq76Y8eOOanVaqZCoZAAAHR3d1OrqqrsAKDzp59+4mZnZ9/8uY4EABDmypt6PWdnZ2Lq1Km9P/zwA5vJZBomT57c5+rqqgcAOHDggNupU6cmGQwGuH37NkOlUjF9fX0nzO1YGAyGYd68efcAACZPntyvVqsZNTU1zMjIyC7j2Qe5XH6voKDAHoB8nJC1w0jGg7u7u5bD4RDG/587d87Bkv4iq5dAINCaKjvU8R8eb8Opw3AZl3C0tbVRZ86cKc7Pz2+rqqpiWlLfixcvsiMjI7scHR2J0NDQXi8vrxFv4kLWv8OFCTRCCKFHLF26VLP/YKa3wWAACoVCWs5gMEBz6QVY8tHmEa07/OKLL1yWLl1q9dnnwcLCwnoZDIYhPz/fHgCAQqE88KPX6ylk5b28vLQBAQG9QqGwHwAgODj4Xmlpqb1UKu3Py8urPHPmDCcrK8v54MGD7tevX79h7dgdHR0fSWJiYmI6Tp48eXO4x7C0/LJly9q+/vprHpPJNCxdurQN4P7p/dzcXMdLly6Vc7lcQiaTBRpnqQEAKBSKYbjHtxU6nT4QI4VCgcHxm2JqnJhrh5GMh48//rieSqUavvzyS57xMbL+Gvw+HHzhrqX1MjceTI03a+PxeERMTExHbm4ux8fHp99cPNYYV2TtNtTnwFBwCQdCCKFHhIaG9tIpQHQ2mM+7OhpqgEmjEKGhob0jeZ0TJ044p6amWj2BvnnzJqOpqYkGcH9tcFVVlb2fn5/JWTlz5WNiYrobGhqYzc3NtN7eXkp5eTlbJBL1EQQBVVVVzEWLFnXt2bOnvqGhgWntOpgSGxt7r6CggFNdXc0AAKioqGAalxBERUV17t+/3wUAoKOjg9rQ0EA3V55MYmJi+/fff+90+vTpSa+88ko7AIBGo6HyeDwdl8slCgsLWeXl5Q/cMYXH4+nq6urGpA2safr06d0FBQWcjo4OaktLC+3q1asOCoWC9GwIWTuMdDy4urrqfz5bAADm+5fD4eibm5tpXV1dlJqaGtZI6juS8WBter0eSktL2X5+fv1DxfPwuIqKiuouKCjgdHZ2UouLi1mNjY1DtrM12s0UnIFGCCH0CCqVCnNmz9YoS8+7OPLJ79zWfPU8zJkd204dYq20KWfPnnVgs9mEXC4f8fppMtXV1czXX3/dDwBAr9dTtm7dqjaXQJsr/+GHH96aNWuWWKfTURISEu6Ghob26vV6SE5OntLZ2UkjCIKybds2tbXrYAqfz9ft3btXFR8fL9Tr9WBvb09kZWXdBADdp59+qnr11VcFmZmZ7gwGw5CVlVUTEhLSZ6q8UqmkLlmyZKpGo6H19vZSJRIJZ9u2bfWJiYkaDw8PvZubm1ar1VL5fL4O4P5dUzIzM90CAgKCAgICeqVSaffguDZu3NiclpY2ZefOnd6nT5+uJFtCMN6IxeL+tLS02+Hh4YEAAG+99VaDsc6mkLWDwWCwyngw178bNmxomjt3rkgul9/z9PQ0275KpZJJ1r9kxx9JvJYwroHW6/WUZ555piMxMVFDpVLBXDwPjyuhUNifmpp6JyQkJFAoFPby+fw+FotlMFdfS9rNEhSDYdyfdTEaVqCTJt2/dWd7+6gsR7O62NhYAAA4d+6cTeN4mmEf2N5E7AMLP2ssOjU4FkpKSmrlcnmLuTJffvnlpM279wump/+JdG1g/h/W6z/47YabP98+atRlZGS41NbW2u3Zs8eqFziNlYkev61gu41PtuiXtrY2Ko/HIxobG+nh4eGBarW6dCRf4IejpKTEVS6XC0w998TNQPf09IBerx/4gzzeXblyBTgcjq3DQMimqqqqoKura8K8bwEAurq6gOyewk+K559/vuONDb+Gr38ZTlrGiecC8fHxpBc4IYSQNa1bt86nuLjYAQBg9+7dt0YreR7KE5dAMxgMW4dgEQ6HA25uE/J2mghZzUR8D9BotAn3eWMpJycnor215Yqt4xgsIiKi29/ff8RX3tvaRI/fVrDdxidb9Mvx48frxvL1yDxxCXRERAQATKxTwQg97YqLi20dgsUm0mz5k8SauxXawkSP31aw3canp7lf8C4cCCGEEEIIWeCJm4FGCCH0+Nw8veUtzY3D/hvh6uGlu9PUUDKaMSGE0HiBCTRCCKFHtDQ30sO3nx12+ctb5uDfE4TQUwOXcCCEEEIIIWQBTKARQgjZxJYtWzyEQmFQQEBA0MaNG72G8zsZGRku6enp3kOVa2pqoslkskCxWCyVSCTSo0ePThpJ+W+++cZRIpFIjT8MBiPswoUL9uaOZY34zWGz2aGW/s7777/v3tnZOWH/5g+33ZYtWyaQSCRSV1dXubu7e4hEIpEmJSX5Aoys3UZLQkKCwNvbOxgAQKfTAY/Hk6empvpa+3WOHTvmtHnzZk9rH9douP2Sk5PD5XK50yQSiVQoFAZt2rTJbEwlJSV2EolEymazQ/Py8tjDjWe06zsYnnJDCCE05qqrqxlHjhxxq6ysvG4wGGDq1KmytLS0uxKJxCq3xOLxeMT58+eVTk5ORGNjIz0kJESalJTUTnbvbrLyCQkJHQkJCWUAAHV1dYyZM2eKJ+KdBw4ePOiRlpbWyuVyiaFLT1zZ2dm1AADp6eneHA5H//777zfbOCRSbDabuHjxon17ezvN1dV1VHYC/HmDozHZ5GgoERERXbm5uVVarRaEQqFs9erVrSKRyOT7XS6X95WXl5dFRkaKLXmNsazvhP02ihBCaGLT6/WU3t5eSk9PD4XBYBA8Hk9vrWPb2dkZnJycCACA1tZWWn9/P1Wr1VLS09O9FQqF2NfXV7ZixQpfgUAga2hooJOVH3zML774ghcfH99m/P/27dvd/f39g0QikXT9+vV8a8U+nOOvXLnSRyAQyJYvX+5nfGzHjh3uQqEwSCgUBu3bt88FAODbb7/lSiQS6e3btxmzZs0SSSQSaW1t7ZN9A3MzhttuZOMEAGDOnDlTxWKxVCaTBe7atWvgJvaWjoe4uLj2EydOTMrOzp4UFxc3sKVpVlaWU3BwcKBYLJauWbNmsvHxwTPokZGRYuPMLJvNDjVVr2XLlgm8vLyCH57ZJjt+Tk4ONzo6Wjh//nx/oVAYtGrVKp/ht+zwdXZ2UrVaLYXFYhHm4iGzZcsWjylTpgTNmTNnqo+Pj0ypVDIByOtrqt3M9e9w4Qw0QgihMRcQEKBdu3Zts6+vbwhBEPDee++pPTw8rJZAA9zf8nf69OmSW7du2e3bt6+WxWIZAAAWLlzYrlKpmL6+vv00Gk2Tm5vrkJycrCErb/TVV1+5HD58uNb4/927d3urVKqrPB6PqK+vt/rfU7Lj9/T0UFNSUloPHTqkFggEstraWkZfXx/l0KFD7qWlpWX9/f2UkJAQ6YsvvqhZunRp59KlS8v4fH7wDz/8UOHl5TUqM50TgSXtBkA+Tg4fPlwXEBCg7evro4jF4qCUlJQ2X19fnaXjISIiovsvf/mLs06noyxdurStsLDQob6+nv7BBx94X7hwQcnlcokFCxb4nzx5krt48WLS3T5N1UsgEGizs7NrMzIyXAoLCx2MZYc6fnFxMScvL++GQqHobWlpsepWq4WFhRyJRCJVqVR2P7/3dZbWt7KyknnkyBG3a9eulVVXVzPDw8ODjM+Zqq85ZP073PpgAo0QQmjM3blzh/aPf/zDqaamprS/v58SFRUlefHFFzV+fn5aa70Gj8cjKioqyoqKilhpaWl+K1eubAcAcHZ21nV0dNCM/7a3t9PIytvZ2RkA7q/J7OnpoUZFRQ0s3wgODr6XlJQkWLBggSY5ObnddBQjR3Z8BoNhmDdv3j0AgMmTJ/er1WpGTU0NMzIysss4iy6Xy+8VFBTY8/l83Gb9Z5a0GwD5ODlw4IDbqVOnJhkMBrh9+zbj5yRMN5Lx4O7uruVwOAPLas6dO+egVquZCoVCAgDQ3d1NraqqsgMA0n40VS+BQGDyfTTU8aVSabdCoegFAHB1dbXqF1rjEo62tjbqzJkzxfn5+W1VVVVMS+p78eJFdmRkZJejoyMRGhra6+XlNeIlX2T9O1yYQCOEEBpzOTk5jpMnT+53cXHRAwDIZLLun376ie3n52f19YthYWG9DAbDkJ+fbw8AQKFQHvjR6/UUsvIzZ87sBgD44osvXJYuXdo6uFxeXl7lmTNnOFlZWc4HDx50v379+g1rxk12fDqdPjAzTqFQgCCe6GXNVmNpu5kaJzk5Odzc3FzHS5culXO5XEImkwUajzOS8fDxxx/XU6lUw5dffskzPhYTE9Nx8uTJm6biMdLr/53bWlovsuMDADg6Olo1aTaFx+MRMTExHbm5uRwfH59+c/FQKBSDqcctQdZuQ30ODAXXQCOEEBpz3t7e2itXrjj09PRQurq6KNeuXWMLhcI+ax3/5s2bjKamJhoAgEqloldVVdmbm90eqvyJEyecU1NTBxJogiCgqqqKuWjRoq49e/bUNzQ0MK0V+0iOP3369O6CggJOR0cHtaWlhXb16lUHhUIxMFvu4OCgv3PnjlVPyT8Jhmq3h2k0GiqPx9NxuVyisLCQVV5ebg8w8vHg6uqqd3Z2Hsh4Y2Nj7xUUFHCqq6sZAAAVFRVMlUpFBwDgcDj65uZmWldXF6WmpoY1kvqaO/5Y0ev1UFpayvbz8+sfKh4ej6erq6sbaMuoqKjugoICTmdnJ7W4uJjV2Ng4ZDtbo91MwRlohBBCYy4uLq5r9uzZHVKpVEqlUiElJeVOWFhYr7WOX11dzXz99df9AO5frLh161a1uQTaXPmzZ886sNlsQi6XDyT4BoMBkpOTp3R2dtIIgqBs27ZNba3YR3J8sVjcn5aWdjs8PDwQAOCtt95q4PP5A+udX3vttdvLli2bOmnSJF12dna1r6/vU7sWerCh2u1hCQkJHZmZmW4BAQFBAQEBvVKptBvAeuOBz+fr9u7dq4qPjxfq9Xqwt7cnsrKybgKAbsOGDU1z584VyeXye56enmaXOimVSuaSJUumajQaWm9vL1UikXC2bdtWn5iYqCE7/kjitYRxDbRer6c888wzHYmJiRoqlQrm4tm4cWNzWlralJ07d3qfPn26UigU9qempt4JCQkJFAqFvXw+v4/FYhnM1deSdrMExWB47NnxsTKsQGNjYwEA4Ny5c6MYCnqS4JhBI2HhuLHo1OBYKCkpqZXL5S1kz1MolHALdyIEg8Fw2SrBmZGRkeFSW1trt2fPnobRfq3RMNHjtxVst/HJFv3S1tZG5fF4RGNjIz08PDxQrVaXUqmjs6CipKTEVS6XC0w9hzPQCCGEHuHq4aWzZHtuV4+n9+4OCKGxs27dOp/i4mIHAIDdu3ffGq3keSiYQCOEEHrEnaaGElvHYEpERES3v7+/VTZbsYWJHr+tYLuNT7bol+PHj9eN5euRwQQaIYTQhDERdwEcbKLHbyvYbuPT09wveBcOhBBCCCGELIAJNEIIoSHpdDr43datHjxXj2lb33vPQ6fDJc8IoacXJtAIIYTMKi0ttQsJjQj8+POvvUGxlrb3s6+8Q8IUgaWlpXa2jg0hhGwBE2iEEEImGWedI6OelTZww+zt47ZS7SbLwf4Xv6M2cKbZR0Y9K9363nseg3f3ssSbb77JFwqFQVKpNPDo0aOThvM7GRkZLunp6d5DlWtqaqLJZLJAsVgslUgk0qGOT1b+m2++cZRIJFLjD4PBCLtw4YL98Go48vitpaSkxE4ikUjZbHZoXl4ee6xe19qG227Lli0TSCQSqaurq9zd3T1EIpFIk5KSfAEA2Gx26OhHOjwJCQkCb2/vYID77zMejydPTU31tfbrHDt2zGnz5s2e1j6u0XD7JScnh8vlcqdJJBKpUCgM2rRpk9mYRjpuR7u+g+FFhAghhB5RWlpql5i0wl/drmU5LNxBpTt5DTxHodKAFbSIQp8cRtn72Z+8vzrxN+fjx/5fTXBw8LB3EszLy2Pn5uY6lpWVXW9paaGHhoZK4+PjO3g8nlX2pebxeMT58+eVTk5ORGNjIz0kJESalJTUTqOZ3oyPrHxCQkJHQkJCGQBAXV0dY+bMmeKJdOGUXC7vKy8vL4uMjBTbOpaxkJ2dXQsAkJ6e7s3hcPTvv/9+s41DIsVms4mLFy/at7e301xdXUdlTVRycrIGADSjcWxLRUREdOXm5lZptVoQCoWy1atXt4pEIpN38BjpuB3L+uIMNEIIoUfMmj1XUm8vs7eP2/pA8jwY3ckL7H+xlVrPCrKfNXuuxJLjK5VKO5lM1s1gMMDLy0vn4eHR/69//cvBKsEDgJ2dncHJyYkAAGhtbaX19/dTtVotJT093VuhUIh9fX1lK1as8BUIBLKGhgY6WfnBx/ziiy948fHxbcb/b9++3d3f3z9IJBJJ169fz7dW7EZbtmzxmDJlStCcOXOm+vj4yJRKJRMAYMeOHe5CoTBIKBQG7du3z8VYfs6cOVPFYrFUJpMF7tq1y83a8TwJVq5c6SMQCGTLly/3Mz5mqj3JxgkAeTtbOh7i4uLaT5w4MSk7O3tSXFx7R/xtAAAJ9UlEQVRcu/HxrKwsp+Dg4ECxWCxds2bNZOPjg2fQIyMjxcaZWTabHWqqXsuWLRN4eXkFPzyzTXb8nJwcbnR0tHD+/Pn+QqEwaNWqVT7Db9nh6+zspGq1WgqLxSLMxUOG7H1BVl9T7Wauf4cLZ6ARQgg9gu/r139rEp9NoZifZ6FQaUCZxKdM9hMMe/YZAEAul/d+9NFHXp2dndSmpiZ6TU0Nq6GhgfFYQT+kra2NOn36dMmtW7fs9u3bV8tisQwAAAsXLmxXqVRMX1/ffhqNpsnNzXVITk7WkJU3+uqrr1wOHz5ca/z/7t27vVUq1VUej0fU19db9e9pZWUl88iRI27Xrl0rq66uZoaHhwcB3N+i+dChQ+6lpaVl/f39lJCQEOmLL76o4fP5usOHD9cFBARo+/r6KGKxOCglJaUNt+z+t56eHmpKSkrroUOH1AKBQFZbW8vo6+ujmGpPAPJxQtbOlo6HiIiI7r/85S/OOp2OsnTp0rbCwkKH+vp6+gcffOB94cIFJZfLJRYsWOB/8uRJ7uLFizstqZdAINBmZ2fXZmRkuBQWFg58MR3q+MXFxZy8vLwbCoWit6WlxfTpmhEybuWtUqns1q5d2+zr66uztL5k7wuA+2cfHq6vOWT9O9z6YAKNEELoEUsWxbV+8u0lFvgphj5T2VBMLHkhrm3IcoNERkb2JCUl3VUoFBJPT8/+6dOnd9rb21tl+YYRj8cjKioqyoqKilhpaWl+K1eubAcAcHZ21nV0dNCM/7a3t9PIytvZ2RkA7q/J7OnpoUZFRQ0s3wgODr6XlJQkWLBggSY5ObnddBQjc/HiRXZkZGSXo6MjERoa2uvl5dUPAJCfn8+OjIzsMs6Wy+XyewUFBfZ8Pr/zwIEDbqdOnZpkMBjg9u3bjJ+TA0ygf8ZgMAzz5s27BwAwefLkfrVazaipqWGaak8A8nFC1s4jGQ/u7u5aDoczMO7PnTvnoFarmQqFQgIA0N3dTa2qqrIDANIE2lS9BAKB1lTZoY4vlUq7FQpFLwCAq6vryC5uIGFcwtHW1kadOXOmOD8/v62qqoppSX3J3hcjQda/w4UJNEIIoUcsW7pUs/eTQ952BgNQKBTScgaDAbT1V2Dpkg8sXnf43nvvNb/33nvNAAByuVwyZcqUUdnRLCwsrJfBYBjy8/PtAQAoFMoDP3q9nkJWfubMmd0AAF988YXL0qVLWweXy8vLqzxz5gwnKyvL+eDBg+7Xr1+/MRrxD0dOTg43NzfX8dKlS+VcLpeQyWSBBPHv7yMUCsVg5tefCnQ6faANKBQKDG4fU0yNE3PtPJLx8PHHH9dTqVTDl19+yTM+FhMT03Hy5MmbpuIxGnzhrqX1Ijs+AICjo6NVk2ZTeDweERMT05Gbm8vx8fHpNxePNcYtWbsN9TkwFFwDjRBC6BGhoaG9dnQKoW9Xmy2nb1eDHYNGhIaG9lr6Gk1NTTQAgO+++46j0Wjozz77bPcIw33EzZs3Gcbjq1QqelVVlb2fn5/JWbnhlD9x4oRzamrqQAJNEARUVVUxFy1a1LVnz576hoYGprViBwCIiorqLigo4HR2dlKLi4tZjY2NTACA6dOndxcUFHA6OjqoLS0ttKtXrzooFIoejUZD5fF4Oi6XSxQWFrLKy8sfuFMIj8fT1dXVWTXGJwFZe5KVJ2vnkY4HV1dXvbOz80DGGxsbe6+goIBTXV3NAACoqKhgqlQqOgAAh8PRNzc307q6uig1NTWskdTX3PHHil6vh9LSUrafn1//UPE8PG7J3hfmWKPdTMEZaIQQQo+gUqkwe3as5nvVFRc6j/xaon51Mfxidmw7lWr5fExSUtKUuro6Ozqdbvjzn/9cM5JjkKmurma+/vrrfgAAer2esnXrVrW5BNpc+bNnzzqw2WxCLpcPrPM2GAyQnJw8pbOzk0YQBGXbtm3mv2lYSCgU9qempt4JCQkJFAqFvXw+v4/FYhn8/Py0aWlpt8PDwwMBAN56660GPp+vS0hI6MjMzHQLCAgICggI6JVKpQ98Gdm4cWNzWlralJ07d3qfPn26kuwU/9NGLBb3m2pPsvJk7Wyt8cDn83V79+5VxcfHC/V6Pdjb2xNZWVk3AUC3YcOGprlz54rkcvk9T09Ps/2nVCqZS5YsmarRaGi9vb1UiUTC2bZtW31iYqKG7PgjidcSxjXQer2e8swzz3QkJiZqqFQqmIvn4XFL9r4wV19L2s0SFINhwpzVGVagsbGxAABw7ty5UQwFPUlwzKCRsHDcWHRqcCyUlJTUyuXyFnNlvvzyy0lvbvm9wG7uu6RrA/v+uV3/yY63blpy8c3jyMjIcKmtrbXbs2dPw1i8nrVZEn9bWxuVx+MRjY2N9PDw8EC1Wl1qzS8ZE8lE7/cnlS36ZSzfFyUlJa5yuVxg6jmcgUYIIWTS888/3/Efb/4KmjNfIi3jOMkZ4uPjSS9wQiO3bt06n+LiYgcAgN27d996WpNnhAYbL+8LTKARQgiZ5OTkRGja7l6xdRyDRUREdPv7+4/KxYZjwZL4jx8/Xjfa8UwUE73fn1S26Jfx8r7ABBohhNCEMZF2ATRlosdvK9hu49PT3C94PgghhJ5OBEEQ425tNkIIjQc/fz6S3hMQE2iEEHo6Xbtz544TJtEIIfQggiAod+7ccQKAa2RlcAkHQgg9hXQ63ZqmpqbMpqYmGeBkCkIIDUYAwDWdTreGrMATdxu7yZMnQ1dXF0ybNm2040FPiCtX7l8jhWMGWeLKlSvA4XBArR7W7V5xlhchhJ4gT9wMtJubm61DQBMMh8OxdQhoAuJwOPh5gxBCT6knbgYaIYTGIZyBRgihJwiue0MIIYQQQsgCmEAjhBBCCCFkAUygEUIIIYQQsgAm0AghhBBCCFkAE2iEEEIIIYQsgAk0QgghhBBCFsAEGiGEEEIIIQtgAo0QQgghhJAFJtJOhLgRAUIIIYQQsjmcgUYIIYQQQsgCmEAjhBBCCCFkAUygEUIIIYQQsgAm0AghhBBCCFkAE2iEEEIIIYQsgAk0QgghhBBCFsAEGiGEEEIIIQtgAo0QQgghhJAFMIFGCCGEEELIAphAI4QQQgghZAFMoBFCCCGEELIAJtAIIYQQQghZABNohBBCCCGELIAJNEIIIYQQQhbABBohhBBCCCELYAKNEEIIIYSQBTCBRgghhBBCyAKYQCOEEEIIIWQBTKARQgghhBCyACbQCCGEEEIIWQATaIQQQgghhCyACTRCCCGEEEIWwAQaIYQQQgghC2ACjRBCCCGEkAUwgUYIIYQQQsgCmEAjhBBCCCFkAUygEUIIIYQQsgAm0AghhBBCCFkAE2iEEEIIIYQsgAk0QgghhBBCFsAEGiGEEEIIIQtgAo0QQgghhJAFMIFGCCGEEELIAv8f0NmINKaCxJwAAAAASUVORK5CYII=\n", 472 | "text/plain": [ 473 | "
" 474 | ] 475 | }, 476 | "metadata": { 477 | "needs_background": "light" 478 | }, 479 | "output_type": "display_data" 480 | }, 481 | { 482 | "name": "stdout", 483 | "output_type": "stream", 484 | "text": [ 485 | "2018-07-14 Belgium 2-0 England 82'\n" 486 | ] 487 | }, 488 | { 489 | "data": { 490 | "image/png": "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\n", 491 | "text/plain": [ 492 | "
" 493 | ] 494 | }, 495 | "metadata": { 496 | "needs_background": "light" 497 | }, 498 | "output_type": "display_data" 499 | } 500 | ], 501 | "source": [ 502 | "import matplotsoccer\n", 503 | "\n", 504 | "for shot in list(atomic_actions[(atomic_actions.type_name == \"goal\")].index):\n", 505 | " a = atomic_actions[shot-8:shot+1].copy()\n", 506 | "\n", 507 | " a[\"start_x\"] = a.x\n", 508 | " a[\"start_y\"] = a.y\n", 509 | " a[\"end_x\"] = a.x + a.dx\n", 510 | " a[\"end_y\"] = a.y + a.dy\n", 511 | "\n", 512 | " g = list(games[games.game_id == a.game_id.values[0]].itertuples())[0]\n", 513 | " minute = int((a.period_id.values[0]-1)*45 +a.time_seconds.values[0] // 60)\n", 514 | " game_info = f\"{g.match_date} {g.home_team_name} {g.home_score}-{g.away_score} {g.away_team_name} {minute + 1}'\"\n", 515 | " print(game_info)\n", 516 | "\n", 517 | " def nice_time(row):\n", 518 | " minute = int((row.period_id-1)*45 +row.time_seconds // 60)\n", 519 | " second = int(row.time_seconds % 60)\n", 520 | " return f\"{minute}m{second}s\"\n", 521 | "\n", 522 | " a[\"nice_time\"] = a.apply(nice_time,axis=1)\n", 523 | " labels = a[[\"nice_time\", \"type_name\", \"player\", \"team_name\"]]\n", 524 | "\n", 525 | " matplotsoccer.actions(\n", 526 | " location=a[[\"start_x\", \"start_y\", \"end_x\", \"end_y\"]],\n", 527 | " action_type=a.type_name,\n", 528 | " team= a.team_name,\n", 529 | " label=labels,\n", 530 | " labeltitle=[\"time\",\"actiontype\",\"player\",\"team\"],\n", 531 | " zoom=False,\n", 532 | " figsize=6\n", 533 | " )" 534 | ] 535 | } 536 | ], 537 | "metadata": { 538 | "kernelspec": { 539 | "display_name": "Python 3", 540 | "language": "python", 541 | "name": "python3" 542 | }, 543 | "language_info": { 544 | "codemirror_mode": { 545 | "name": "ipython", 546 | "version": 3 547 | }, 548 | "file_extension": ".py", 549 | "mimetype": "text/x-python", 550 | "name": "python", 551 | "nbconvert_exporter": "python", 552 | "pygments_lexer": "ipython3", 553 | "version": "3.7.1" 554 | }, 555 | "varInspector": { 556 | "cols": { 557 | "lenName": 16, 558 | "lenType": 16, 559 | "lenVar": 40 560 | }, 561 | "kernels_config": { 562 | "python": { 563 | "delete_cmd_postfix": "", 564 | "delete_cmd_prefix": "del ", 565 | "library": "var_list.py", 566 | "varRefreshCmd": "print(var_dic_list())" 567 | }, 568 | "r": { 569 | "delete_cmd_postfix": ") ", 570 | "delete_cmd_prefix": "rm(", 571 | "library": "var_list.r", 572 | "varRefreshCmd": "cat(var_dic_list()) " 573 | } 574 | }, 575 | "types_to_exclude": [ 576 | "module", 577 | "function", 578 | "builtin_function_or_method", 579 | "instance", 580 | "_Feature" 581 | ], 582 | "window_display": false 583 | } 584 | }, 585 | "nbformat": 4, 586 | "nbformat_minor": 2 587 | } 588 | -------------------------------------------------------------------------------- /notebooks/paper-experiment1-deanonymizing-players.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "%load_ext autoreload\n", 10 | "%autoreload 2\n", 11 | "import os; import sys; sys.path.insert(0, '../')\n", 12 | "import pandas as pd\n", 13 | "import tqdm\n", 14 | "import pickle\n", 15 | "\n", 16 | "import numpy as np\n", 17 | "import warnings" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 2, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "### Configure file and folder names\n", 27 | "data_h5 = \"../data/paper/soccermix_all_data.h5\"\n", 28 | "\n", 29 | "d_weights = \"../data/paper/soccermix_all_dirweights.pkl\"\n", 30 | "\n", 31 | "spadl_h5 = \"../data/tomd/spadl-statsbomb.h5\"" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 3, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "X = pd.read_hdf(data_h5, \"X\")" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 4, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "def loadall(filename):\n", 50 | " with open(filename, \"rb\") as f:\n", 51 | " while True:\n", 52 | " try:\n", 53 | " yield pickle.load(f)\n", 54 | " except EOFError:\n", 55 | " break\n", 56 | "\n", 57 | "d_w = loadall(d_weights)" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 5, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "dir_weights = next(d_w)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 6, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "games = pd.read_hdf(spadl_h5, \"games\")\n", 76 | "\n", 77 | "games_1819 = games[games.season_name == '2018/2019']\n", 78 | "games_1718 = games[games.season_name == '2017/2018']" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 7, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "players = pd.read_hdf(spadl_h5, \"players\")\n", 88 | "pg = pd.read_hdf(spadl_h5, \"player_games\")" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 8, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "pg_1819 = pg[pg.game_id.isin(games_1819.game_id)]\n", 98 | "pg_1718 = pg[pg.game_id.isin(games_1718.game_id)]" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 9, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "players_1819 = players[players.player_id.isin(pg_1819.player_id)]\n", 108 | "players_1718 = players[players.player_id.isin(pg_1718.player_id)]" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 10, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "mp_1819 = pg_1819[[\"player_id\", \"minutes_played\"]].groupby(\"player_id\").sum().reset_index()\n", 118 | "mp_1718 = pg_1718[[\"player_id\", \"minutes_played\"]].groupby(\"player_id\").sum().reset_index()" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 11, 124 | "metadata": {}, 125 | "outputs": [ 126 | { 127 | "name": "stderr", 128 | "output_type": "stream", 129 | "text": [ 130 | "100%|██████████| 515/515 [02:00<00:00, 4.29it/s]\n", 131 | "100%|██████████| 505/505 [01:46<00:00, 4.73it/s]\n" 132 | ] 133 | } 134 | ], 135 | "source": [ 136 | "# Get player vectors\n", 137 | "\n", 138 | "merged_weights = dir_weights.copy()\n", 139 | "merged_weights[\"player_id\"] = X.player_id.values\n", 140 | "merged_weights[\"game_id\"] = X.game_id.values\n", 141 | "\n", 142 | "vectors_1718 = {}\n", 143 | "for p in tqdm.tqdm(list(players_1718.player_id.unique())):\n", 144 | " vectors_1718[int(p)] = merged_weights.loc[((merged_weights.player_id == p)\n", 145 | " & (merged_weights.game_id.isin(games_1718.game_id))),\n", 146 | " dir_weights.columns].sum().values\n", 147 | " \n", 148 | "vectors_1819 = {}\n", 149 | "for p in tqdm.tqdm(list(players_1819.player_id.unique())):\n", 150 | " vectors_1819[int(p)] = merged_weights.loc[((merged_weights.player_id == p)\n", 151 | " & (merged_weights.game_id.isin(games_1819.game_id))),\n", 152 | " dir_weights.columns].sum().values\n", 153 | " \n", 154 | "vectors_1718_pd = pd.concat({k: pd.DataFrame(v).T for k,v in vectors_1718.items()}).droplevel(level=1)\n", 155 | "vectors_1718_pd.index.name = \"player_id\"\n", 156 | "vectors_1718_pd.columns = dir_weights.columns\n", 157 | "\n", 158 | "vectors_1819_pd = pd.concat({k: pd.DataFrame(v).T for k,v in vectors_1819.items()}).droplevel(level=1)\n", 159 | "vectors_1819_pd.index.name = \"player_id\"\n", 160 | "vectors_1819_pd.columns = dir_weights.columns" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 12, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "# Normalize vectors per 90 min game time\n", 170 | "\n", 171 | "vectors_1718_norm = pd.merge(vectors_1718_pd, mp_1718, left_index=True, right_on='player_id').set_index('player_id')\n", 172 | "df1 = vectors_1718_norm.loc[:, dir_weights.columns] * 90\n", 173 | "vectors_1718_norm.loc[:, dir_weights.columns] = df1.divide(vectors_1718_norm.minutes_played, axis='rows')\n", 174 | "vectors_1718_norm.drop(columns=['minutes_played'], inplace=True)\n", 175 | "\n", 176 | "vectors_1819_norm = pd.merge(vectors_1819_pd, mp_1819, left_index=True, right_on='player_id').set_index('player_id')\n", 177 | "df1 = vectors_1819_norm.loc[:, dir_weights.columns] * 90\n", 178 | "vectors_1819_norm.loc[:, dir_weights.columns] = df1.divide(vectors_1819_norm.minutes_played, axis='rows')\n", 179 | "vectors_1819_norm.drop(columns=['minutes_played'], inplace=True)" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 13, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "# Code below mainly from Pieter's implementation of this experiment with soccer vectors\n", 189 | "# https://github.com/probberechts/soccer-player-vectors-thesis/blob/master/notebooks/5-experiments.ipynb\n", 190 | "\n", 191 | "# Select correct players to test on \n", 192 | "\n", 193 | "train_players = pg_1718.groupby('player_id').agg({\n", 194 | " 'minutes_played': 'sum',\n", 195 | " 'team_id': set\n", 196 | "}).merge(players_1718, on=\"player_id\", how='left')\n", 197 | "\n", 198 | "test_players = pg_1819.groupby('player_id').agg({\n", 199 | " 'minutes_played': 'sum',\n", 200 | " 'team_id': set\n", 201 | "}).merge(players_1819, on=\"player_id\", how='left')" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": 14, 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "all_players = pd.merge(train_players, test_players, on=\"player_id\", suffixes=(\"_train\", \"_test\"))\n", 211 | "all_players['nb_teams'] = all_players.apply(lambda x: len(x.team_id_train | x.team_id_test), axis=1)\n", 212 | "all_players = all_players[all_players.nb_teams == 1]" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 15, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "# Only players who played >= 900 minutes in both train and test season\n", 222 | "all_players = all_players[(all_players.minutes_played_train >= 900) & (all_players.minutes_played_test >= 900)]" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": 16, 228 | "metadata": {}, 229 | "outputs": [ 230 | { 231 | "name": "stdout", 232 | "output_type": "stream", 233 | "text": [ 234 | "Number of players: 193\n" 235 | ] 236 | } 237 | ], 238 | "source": [ 239 | "all_players = all_players.player_id.unique()\n", 240 | "print(\"Number of players: \", len(all_players))" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 17, 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "# Compute pairwise distances\n", 250 | "\n", 251 | "from sklearn.metrics import pairwise_distances\n", 252 | "from sklearn import preprocessing\n", 253 | "\n", 254 | "# D = pairwise_distances(\n", 255 | "# vectors_1718_norm.loc[all_players],\n", 256 | "# vectors_1819_norm.loc[all_players],\n", 257 | "# metric='manhattan'\n", 258 | "# )\n", 259 | "\n", 260 | "D = pairwise_distances(\n", 261 | " preprocessing.normalize(vectors_1718_norm.loc[all_players], norm=\"l1\"),\n", 262 | " preprocessing.normalize(vectors_1819_norm.loc[all_players], norm=\"l1\"),\n", 263 | " metric=\"manhattan\")\n", 264 | "\n", 265 | "# sort each row\n", 266 | "k_d = np.sort(D, axis = 1) \n", 267 | "# sort each row and replace distances by index\n", 268 | "k_i = np.argsort(D, axis = 1) \n", 269 | "# replace indices by player ids\n", 270 | "p_i = np.take(all_players, k_i, axis = 0)" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 18, 276 | "metadata": {}, 277 | "outputs": [ 278 | { 279 | "data": { 280 | "text/plain": [ 281 | "array([ 13, 1, 4, 0, 4, 0, 90, 0, 0, 0, 0, 0, 0,\n", 282 | " 4, 0, 0, 0, 0, 20, 6, 0, 0, 13, 0, 1, 0,\n", 283 | " 5, 0, 0, 29, 0, 3, 2, 1, 15, 1, 1, 142, 0,\n", 284 | " 1, 1, 0, 0, 0, 3, 0, 0, 0, 6, 7, 1, 0,\n", 285 | " 0, 4, 5, 0, 0, 0, 0, 0, 2, 7, 0, 15, 0,\n", 286 | " 7, 0, 5, 2, 0, 0, 11, 5, 12, 0, 0, 4, 0,\n", 287 | " 0, 2, 0, 1, 0, 0, 0, 60, 0, 8, 3, 0, 8,\n", 288 | " 2, 0, 0, 0, 10, 13, 0, 0, 3, 25, 27, 23, 0,\n", 289 | " 2, 0, 0, 34, 0, 1, 20, 1, 0, 0, 1, 0, 2,\n", 290 | " 16, 3, 0, 0, 0, 13, 1, 11, 11, 9, 0, 8, 3,\n", 291 | " 158, 0, 0, 106, 0, 0, 0, 5, 0, 4, 0, 40, 1,\n", 292 | " 0, 90, 6, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4,\n", 293 | " 1, 0, 0, 0, 2, 0, 6, 0, 1, 0, 0, 24, 11,\n", 294 | " 37, 11, 17, 4, 4, 73, 53, 1, 1, 6, 2, 0, 0,\n", 295 | " 31, 0, 3, 0, 0, 0, 15, 14, 15, 3, 6])" 296 | ] 297 | }, 298 | "execution_count": 18, 299 | "metadata": {}, 300 | "output_type": "execute_result" 301 | } 302 | ], 303 | "source": [ 304 | "rs = np.argmax(np.array([p_i[i,:] == all_players[i] for i in range(p_i.shape[0])]), axis=1)\n", 305 | "rs" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 19, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "def mean_reciprocal_rank(rs):\n", 315 | " return np.mean(1. / (rs + 1))\n", 316 | "\n", 317 | "def top_k(rs, k):\n", 318 | " return (rs < k).sum() / len(rs)" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": 20, 324 | "metadata": {}, 325 | "outputs": [ 326 | { 327 | "data": { 328 | "text/plain": [ 329 | "0.5885390745244184" 330 | ] 331 | }, 332 | "execution_count": 20, 333 | "metadata": {}, 334 | "output_type": "execute_result" 335 | } 336 | ], 337 | "source": [ 338 | "mean_reciprocal_rank(rs)" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": 21, 344 | "metadata": {}, 345 | "outputs": [ 346 | { 347 | "data": { 348 | "text/plain": [ 349 | "0.8082901554404145" 350 | ] 351 | }, 352 | "execution_count": 21, 353 | "metadata": {}, 354 | "output_type": "execute_result" 355 | } 356 | ], 357 | "source": [ 358 | "top_k(rs, 10)" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": 22, 364 | "metadata": {}, 365 | "outputs": [ 366 | { 367 | "name": "stdout", 368 | "output_type": "stream", 369 | "text": [ 370 | "0.7150259067357513\n", 371 | "0.6269430051813472\n", 372 | "0.48186528497409326\n" 373 | ] 374 | } 375 | ], 376 | "source": [ 377 | "print(top_k(rs, 5))\n", 378 | "print(top_k(rs, 3))\n", 379 | "print(top_k(rs, 1))" 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": {}, 385 | "source": [ 386 | "# Get similar players to player" 387 | ] 388 | }, 389 | { 390 | "cell_type": "code", 391 | "execution_count": 23, 392 | "metadata": {}, 393 | "outputs": [], 394 | "source": [ 395 | "def get_similar_players(player_id):\n", 396 | " player_index = np.where(all_players == player_id)[0][0]\n", 397 | " print(player_index)\n", 398 | " sims = p_i[player_index,:]\n", 399 | " names = players_1819.set_index(\"player_id\").loc[sims, \"player_name\"].values\n", 400 | " dists = k_d[player_index,:]\n", 401 | " return pd.DataFrame({\"name\": names, \"dist\": dists})" 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": 24, 407 | "metadata": {}, 408 | "outputs": [ 409 | { 410 | "name": "stdout", 411 | "output_type": "stream", 412 | "text": [ 413 | "55 3202\n", 414 | "Name: player_id, dtype: int64\n", 415 | "48 3202\n", 416 | "Name: player_id, dtype: int64\n", 417 | "60 3237\n", 418 | "Name: player_id, dtype: int64\n", 419 | "53 3237\n", 420 | "Name: player_id, dtype: int64\n" 421 | ] 422 | } 423 | ], 424 | "source": [ 425 | "print(train_players[train_players.player_name.str.contains('Jesus')].player_id)\n", 426 | "print(test_players[test_players.player_name.str.contains('Jesus')].player_id)\n", 427 | "\n", 428 | "print(train_players[train_players.player_name.str.contains('Agüero')].player_id)\n", 429 | "print(test_players[test_players.player_name.str.contains('Agüero')].player_id)" 430 | ] 431 | }, 432 | { 433 | "cell_type": "code", 434 | "execution_count": 25, 435 | "metadata": {}, 436 | "outputs": [ 437 | { 438 | "name": "stdout", 439 | "output_type": "stream", 440 | "text": [ 441 | "43\n" 442 | ] 443 | }, 444 | { 445 | "data": { 446 | "text/html": [ 447 | "
\n", 448 | "\n", 461 | "\n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | "
namedist
0Sergio Leonel Agüero del Castillo0.208176
1Marko Arnautović0.293974
2Gabriel Fernando de Jesus0.319744
3Cenk Tosun0.322811
4Jamie Vardy0.349722
.........
188Alex McCarthy1.914147
189Martin Dúbravka1.914678
190Asmir Begović1.915225
191Hugo Lloris1.924485
192David de Gea Quintana1.924613
\n", 527 | "

193 rows × 2 columns

\n", 528 | "
" 529 | ], 530 | "text/plain": [ 531 | " name dist\n", 532 | "0 Sergio Leonel Agüero del Castillo 0.208176\n", 533 | "1 Marko Arnautović 0.293974\n", 534 | "2 Gabriel Fernando de Jesus 0.319744\n", 535 | "3 Cenk Tosun 0.322811\n", 536 | "4 Jamie Vardy 0.349722\n", 537 | ".. ... ...\n", 538 | "188 Alex McCarthy 1.914147\n", 539 | "189 Martin Dúbravka 1.914678\n", 540 | "190 Asmir Begović 1.915225\n", 541 | "191 Hugo Lloris 1.924485\n", 542 | "192 David de Gea Quintana 1.924613\n", 543 | "\n", 544 | "[193 rows x 2 columns]" 545 | ] 546 | }, 547 | "execution_count": 25, 548 | "metadata": {}, 549 | "output_type": "execute_result" 550 | } 551 | ], 552 | "source": [ 553 | "get_similar_players(3237) # Similar to Aguero" 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": 26, 559 | "metadata": { 560 | "scrolled": false 561 | }, 562 | "outputs": [ 563 | { 564 | "name": "stdout", 565 | "output_type": "stream", 566 | "text": [ 567 | "39\n" 568 | ] 569 | }, 570 | { 571 | "data": { 572 | "text/html": [ 573 | "
\n", 574 | "\n", 587 | "\n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | " \n", 631 | " \n", 632 | " \n", 633 | " \n", 634 | " \n", 635 | " \n", 636 | " \n", 637 | " \n", 638 | " \n", 639 | " \n", 640 | " \n", 641 | " \n", 642 | " \n", 643 | " \n", 644 | " \n", 645 | " \n", 646 | " \n", 647 | " \n", 648 | " \n", 649 | " \n", 650 | " \n", 651 | " \n", 652 | "
namedist
0Sergio Leonel Agüero del Castillo0.232574
1Gabriel Fernando de Jesus0.235393
2Jamie Vardy0.289722
3Harry Kane0.297915
4Troy Deeney0.314603
.........
188Alex McCarthy1.896625
189Mathew Ryan1.897377
190Asmir Begović1.899667
191Hugo Lloris1.905453
192David de Gea Quintana1.912968
\n", 653 | "

193 rows × 2 columns

\n", 654 | "
" 655 | ], 656 | "text/plain": [ 657 | " name dist\n", 658 | "0 Sergio Leonel Agüero del Castillo 0.232574\n", 659 | "1 Gabriel Fernando de Jesus 0.235393\n", 660 | "2 Jamie Vardy 0.289722\n", 661 | "3 Harry Kane 0.297915\n", 662 | "4 Troy Deeney 0.314603\n", 663 | ".. ... ...\n", 664 | "188 Alex McCarthy 1.896625\n", 665 | "189 Mathew Ryan 1.897377\n", 666 | "190 Asmir Begović 1.899667\n", 667 | "191 Hugo Lloris 1.905453\n", 668 | "192 David de Gea Quintana 1.912968\n", 669 | "\n", 670 | "[193 rows x 2 columns]" 671 | ] 672 | }, 673 | "execution_count": 26, 674 | "metadata": {}, 675 | "output_type": "execute_result" 676 | } 677 | ], 678 | "source": [ 679 | "get_similar_players(3202) # Similar to Jesus" 680 | ] 681 | } 682 | ], 683 | "metadata": { 684 | "kernelspec": { 685 | "display_name": "Python 3", 686 | "language": "python", 687 | "name": "python3" 688 | }, 689 | "language_info": { 690 | "codemirror_mode": { 691 | "name": "ipython", 692 | "version": 3 693 | }, 694 | "file_extension": ".py", 695 | "mimetype": "text/x-python", 696 | "name": "python", 697 | "nbconvert_exporter": "python", 698 | "pygments_lexer": "ipython3", 699 | "version": "3.7.1" 700 | }, 701 | "varInspector": { 702 | "cols": { 703 | "lenName": 16, 704 | "lenType": 16, 705 | "lenVar": 40 706 | }, 707 | "kernels_config": { 708 | "python": { 709 | "delete_cmd_postfix": "", 710 | "delete_cmd_prefix": "del ", 711 | "library": "var_list.py", 712 | "varRefreshCmd": "print(var_dic_list())" 713 | }, 714 | "r": { 715 | "delete_cmd_postfix": ") ", 716 | "delete_cmd_prefix": "rm(", 717 | "library": "var_list.r", 718 | "varRefreshCmd": "cat(var_dic_list()) " 719 | } 720 | }, 721 | "types_to_exclude": [ 722 | "module", 723 | "function", 724 | "builtin_function_or_method", 725 | "instance", 726 | "_Feature" 727 | ], 728 | "window_display": false 729 | } 730 | }, 731 | "nbformat": 4, 732 | "nbformat_minor": 4 733 | } 734 | -------------------------------------------------------------------------------- /vis.py: -------------------------------------------------------------------------------- 1 | from scipy import linalg 2 | import matplotlib.pyplot as plt 3 | import matplotlib as mpl 4 | import matplotsoccer as mps 5 | import numpy as np 6 | import math 7 | 8 | 9 | def dual_axes(figsize=4): 10 | fig, axs = plt.subplots(1, 2) 11 | fig.set_size_inches((figsize * 3, figsize)) 12 | return axs[0], axs[1] 13 | 14 | 15 | def loc_angle_axes(figsize=4): 16 | fig, _axs = plt.subplots(1, 2) 17 | fig.set_size_inches((figsize * 3, figsize)) 18 | 19 | axloc = plt.subplot(121) 20 | axloc = field(axloc) 21 | axpol = plt.subplot(122, projection="polar") 22 | # axpol.set_rticks(np.linspace(0, 2, 21)) 23 | return axloc, axpol 24 | 25 | 26 | def field(ax): 27 | ax = mps.field(ax=ax, show=False) 28 | ax.set_xlim(-1, 105 + 1) 29 | ax.set_ylim(-1, 68 + 1) 30 | return ax 31 | 32 | 33 | def movement(ax): 34 | plt.axis("on") 35 | plt.axis("scaled") 36 | ax.spines["left"].set_position("center") 37 | ax.spines["bottom"].set_position("center") 38 | ax.spines["right"].set_color("none") 39 | ax.spines["top"].set_color("none") 40 | ax.set_xlim(-60, 60) 41 | ax.set_ylim(-60, 60) 42 | return ax 43 | 44 | 45 | def polar(ax): 46 | plt.axis("on") 47 | ax.set_xlim(-3.2, 3.2) 48 | ax.spines["left"].set_position("center") 49 | ax.spines["right"].set_color("none") 50 | ax.spines["top"].set_color("none") 51 | return ax 52 | 53 | 54 | ################################## 55 | # MODEL-BASED VISUALIZATION 56 | ################################# 57 | 58 | 59 | def show_location_model(loc_model, show=True, figsize=6): 60 | ax = mps.field(show=False, figsize=figsize) 61 | 62 | norm_strengths = loc_model.priors / np.max(loc_model.priors) * 0.8 63 | for strength, gauss, color in zip(norm_strengths, loc_model.submodels, colors * 10): 64 | add_ellips(ax, gauss.mean, gauss.cov, color=color, alpha=strength) 65 | if show: 66 | plt.show() 67 | 68 | 69 | def show_direction_model(gauss, dir_models, show=True, figsize=6): 70 | ax = mps.field(show=False, figsize=figsize) 71 | 72 | # for gauss in loc_model.submodels: 73 | add_ellips(ax, gauss.mean, gauss.cov, alpha=0.5) 74 | 75 | x, y = gauss.mean 76 | 77 | for vonmises in dir_models.submodels: 78 | dx = np.cos(vonmises.loc)[0] 79 | dy = np.sin(vonmises.loc)[0] 80 | r = vonmises.R[0] 81 | add_arrow(ax, x, y, 10*dx, 10*dy, 82 | linewidth=0.5) 83 | 84 | if show: 85 | plt.show() 86 | 87 | 88 | 89 | def show_location_models(loc_models, figsize=6): 90 | """ 91 | Model-based visualization 92 | """ 93 | for model in loc_models: 94 | print(model.name, model.n_components) 95 | show_location_model(model, figsize=6) 96 | 97 | 98 | 99 | def show_all_models(loc_models, dir_models): 100 | 101 | for loc_model in loc_models: 102 | print(loc_model.name, loc_model.n_components) 103 | ax = mps.field(show=False, figsize=8) 104 | 105 | am_subclusters = [] 106 | for a, _ in enumerate(loc_model.submodels): 107 | for dir_model in dir_models: 108 | if f"{loc_model.name}_{a}" == dir_model.name: 109 | am_subclusters.append(dir_model.n_components) 110 | 111 | am_subclusters = np.array(am_subclusters) 112 | 113 | for i, gauss in enumerate(loc_model.submodels): 114 | 115 | if (am_subclusters == 1).all(): 116 | add_ellips(ax, gauss.mean, gauss.cov, alpha=0.5) 117 | 118 | else: 119 | add_ellips(ax, gauss.mean, gauss.cov, color='grey') 120 | 121 | x, y = gauss.mean 122 | for dir_model in dir_models: 123 | if f"{loc_model.name}_{i}" == dir_model.name: 124 | print(dir_model.name, dir_model.n_components) 125 | 126 | for j, vonmises in enumerate(dir_model.submodels): 127 | dx = np.cos(vonmises.loc)[0] 128 | dy = np.sin(vonmises.loc)[0] 129 | r = vonmises.R[0] 130 | add_arrow(ax, x, y, 10*dx, 10*dy, 131 | linewidth=0.5) 132 | 133 | plt.show() 134 | 135 | 136 | def show_direction_models(loc_models, dir_models, figsize=8): 137 | """ 138 | Model-based visualization 139 | """ 140 | for loc_model in loc_models: 141 | print(loc_model.name, loc_model.n_components) 142 | ax = mps.field(show=False, figsize=figsize) 143 | 144 | norm_strengths = loc_model.priors / np.max(loc_model.priors) * 0.8 145 | for i, (strength, gauss) in enumerate(zip(norm_strengths, loc_model.submodels)): 146 | add_ellips(ax, gauss.mean, gauss.cov, alpha=strength) 147 | 148 | x, y = gauss.mean 149 | for dir_model in dir_models: 150 | if f"{loc_model.name}_{i}" == dir_model.name: 151 | print(dir_model.name, dir_model.n_components) 152 | dir_norm_strengths = ( 153 | dir_model.priors / np.max(dir_model.priors) * 0.8 154 | ) 155 | for strength, vonmises in zip( 156 | dir_norm_strengths, dir_model.submodels 157 | ): 158 | dx = np.cos(vonmises.loc)[0] 159 | dy = np.sin(vonmises.loc)[0] 160 | r = vonmises.R[0] 161 | add_arrow( 162 | ax, 163 | x, 164 | y, 165 | 10 * r * dx, 166 | 10 * r * dy, 167 | alpha=strength, 168 | threshold=0, 169 | ) 170 | plt.show() 171 | 172 | 173 | def add_ellips(ax, mean, covar, color=None, alpha=0.7): 174 | v, w = linalg.eigh(covar) 175 | v = 2.0 * np.sqrt(2.0) * np.sqrt(v) 176 | u = w[0] / linalg.norm(w[0]) 177 | 178 | # Plot an ellipse to show the Gaussian component 179 | angle = np.arctan(u[1] / u[0]) 180 | angle = 180.0 * angle / np.pi # convert to degrees 181 | ell = mpl.patches.Ellipse(mean, v[0], v[1], 180.0 + angle, color=color) 182 | # ell.set_clip_box(axs[0].bbox) 183 | ell.set_alpha(alpha) 184 | ell.width = max(ell.width, 3) 185 | ell.height = max(ell.height, 3) 186 | ax.add_artist(ell) 187 | return ax 188 | 189 | 190 | def add_arrow(ax, x, y, dx, dy, arrowsize=2.5, linewidth=2, threshold=2, alpha=1, fc='black', ec='black'): 191 | if abs(dx) > threshold or abs(dy) > threshold: 192 | return ax.arrow( 193 | x, 194 | y, 195 | dx, 196 | dy, 197 | head_width=arrowsize, 198 | head_length=arrowsize, 199 | linewidth=linewidth, 200 | fc=fc, # colors[i % len(colors)], 201 | ec=ec, # colors[i % len(colors)], 202 | length_includes_head=True, 203 | alpha=alpha, 204 | zorder=3, 205 | ) 206 | 207 | 208 | ###################################################### 209 | # PROBABILITY-DENSITY-FUNCTION BASED VISUALIZATION 210 | ###################################################### 211 | 212 | 213 | def show_direction_models_pdf(loc_models, dir_models): 214 | """ 215 | Probability-density function based visualization 216 | """ 217 | for loc_model in loc_models: 218 | print(loc_model.name, loc_model.n_components) 219 | for i, gauss in enumerate(loc_model.submodels): 220 | # axloc, axpol = dual_axes() 221 | # # vis.add_ellips(axloc,gauss.mean,gauss.cov) 222 | # draw_contour(axloc, gauss, cmap="Blues") 223 | for dir_model in dir_models: 224 | if f"{loc_model.name}_{i}" == dir_model.name: 225 | print(dir_model.name, dir_model.n_components) 226 | 227 | axcol, axpol = loc_angle_axes() 228 | draw_contour(axcol, gauss, cmap="Blues") 229 | draw_vonmises_pdfs(dir_model, axpol) 230 | plt.show() 231 | 232 | 233 | def draw_contour(ax, gauss, n=100, cmap="Blues"): 234 | x = np.linspace(0, 105, n) 235 | y = np.linspace(0, 105, n) 236 | xx, yy = np.meshgrid(x, y) 237 | zz = gauss.pdf(np.array([xx.flatten(), yy.flatten()]).T) 238 | zz = zz.reshape(xx.shape) 239 | ax.contourf(xx, yy, zz, cmap=cmap) 240 | return ax 241 | 242 | 243 | def draw_vonmises_pdfs(model, ax=None,figsize=4,projection="polar",n=200,show=True): 244 | if ax is None: 245 | ax = plt.subplot(111, projection=projection) 246 | plt.gcf().set_size_inches((figsize, figsize)) 247 | x = np.linspace(-np.pi, np.pi, n) 248 | total = np.zeros(x.shape) 249 | for i, (prior, vonmises) in enumerate(zip(model.priors, model.submodels)): 250 | p = prior * vonmises.pdf(x) 251 | p = np.nan_to_num(p) 252 | ax.plot(x, p, linewidth=2, color=(colors * 10)[i],label = f"Component {i}") 253 | total += p 254 | # ax.plot(x, total, linewidth=3, color="black") 255 | return ax 256 | 257 | 258 | ################################# 259 | # DATA-BASED VISUALIZATION 260 | ################################# 261 | 262 | colors = [ 263 | "#377eb8", 264 | "#e41a1c", 265 | "#4daf4a", 266 | "#984ea3", 267 | "#ff7f00", 268 | "#ffff33", 269 | "#a65628", 270 | "#f781bf", 271 | "#999999", 272 | ] 273 | 274 | 275 | def scatter_location_model( 276 | loc_model, actions, W, samplefn="max", tol=0.1, figsize=6, alpha=0.5, show=True 277 | ): 278 | X = actions[["x", "y"]] 279 | probs = loc_model.predict_proba(X, W[loc_model.name].values) 280 | probs = np.nan_to_num(probs) 281 | pos_prob_idx = probs.sum(axis=1) > tol 282 | x = X[pos_prob_idx] 283 | w = probs[pos_prob_idx] 284 | 285 | if loc_model.n_components > len(colors): 286 | means = [m.mean for m in loc_model.submodels] 287 | good_colors = color_submodels(means, colors) 288 | else: 289 | good_colors = colors 290 | c = scattercolors(w, good_colors, samplefn=samplefn) 291 | 292 | ax = mps.field(show=False, figsize=figsize) 293 | ax.scatter(x.x, x.y, c=c, alpha=alpha) 294 | if show: 295 | plt.show() 296 | 297 | def scatter_location_model_black( 298 | loc_model, actions, W, samplefn="max", tol=0.1, figsize=6, alpha=0.5, show=True 299 | ): 300 | X = actions[["x", "y"]] 301 | probs = loc_model.predict_proba(X, W[loc_model.name].values) 302 | probs = np.nan_to_num(probs) 303 | pos_prob_idx = probs.sum(axis=1) > tol 304 | x = X[pos_prob_idx] 305 | w = probs[pos_prob_idx] 306 | 307 | if loc_model.n_components > len(colors): 308 | means = [m.mean for m in loc_model.submodels] 309 | good_colors = color_submodels(means, colors) 310 | else: 311 | good_colors = colors 312 | c = scattercolors(w, good_colors, samplefn=samplefn) 313 | 314 | ax = mps.field(show=False, figsize=figsize) 315 | ax.scatter(x.x, x.y, c="black", alpha=alpha) 316 | if show: 317 | plt.show() 318 | 319 | 320 | def scatter_location_models( 321 | loc_models, actions, W, samplefn="max", tol=0.1, figsize=8, alpha=0.5 322 | ): 323 | """ 324 | Data-based visualization 325 | """ 326 | for model in loc_models: 327 | print(model.name, model.n_components) 328 | X = actions[["x", "y"]] 329 | probs = model.predict_proba(X, W[model.name].values) 330 | probs = np.nan_to_num(probs) 331 | pos_prob_idx = probs.sum(axis=1) > tol 332 | x = X[pos_prob_idx] 333 | w = probs[pos_prob_idx] 334 | 335 | if model.n_components > len(colors): 336 | means = [m.mean for m in model.submodels] 337 | good_colors = color_submodels(means, colors) 338 | else: 339 | good_colors = colors 340 | c = scattercolors(w, good_colors, samplefn=samplefn) 341 | 342 | ax = mps.field(show=False, figsize=figsize) 343 | ax.scatter(x.x, x.y, c=c, alpha=alpha) 344 | plt.show() 345 | 346 | 347 | def scatter_direction_models( 348 | dir_models, actions, X, W, samplefn="max", tol=0.1, figsize=4, alpha=0.5 349 | ): 350 | for model in dir_models: 351 | print(model.name, model.n_components) 352 | probs = model.predict_proba(X, W[model.name].values) 353 | probs = np.nan_to_num(probs) 354 | pos_prob_idx = probs.sum(axis=1) > tol 355 | w = probs[pos_prob_idx] 356 | c = scattercolors(w, samplefn=samplefn) 357 | 358 | axloc, axmov = dual_axes() 359 | field(axloc) 360 | movement(axmov) 361 | 362 | x = actions[pos_prob_idx] 363 | axloc.scatter(x.x, x.y, c=c, alpha=alpha) 364 | axmov.scatter(x.dx, x.dy, c=c, alpha=alpha) 365 | plt.show() 366 | 367 | 368 | def hist_direction_model( 369 | dir_model, 370 | actions, 371 | W, 372 | samplefn="max", 373 | tol=0.1, 374 | figsize=4, 375 | alpha=0.5, 376 | projection="polar", 377 | bins=20, 378 | show=False, 379 | ): 380 | X = actions["mov_angle_a0"] 381 | probs = dir_model.predict_proba(X, W[dir_model.name].values) 382 | probs = np.nan_to_num(probs) 383 | pos_prob_idx = probs.sum(axis=1) > tol 384 | w = probs[pos_prob_idx] 385 | c = scattercolors(w, samplefn=samplefn) 386 | 387 | axpol = plt.subplot(111, projection=projection) 388 | plt.gcf().set_size_inches((figsize, figsize)) 389 | 390 | x = actions[pos_prob_idx] 391 | for p, c in zip(w.T, colors): 392 | p = p.flatten() 393 | axpol.hist(x.mov_angle_a0, weights=p.flatten(), color=c, alpha=alpha, bins=bins) 394 | if show: 395 | plt.show() 396 | 397 | 398 | 399 | 400 | def hist_direction_models( 401 | dir_models, actions, W, samplefn="max", tol=0.1, figsize=4, alpha=0.5 402 | ): 403 | for model in dir_models: 404 | print(model.name, model.n_components) 405 | X = actions["mov_angle_a0"] 406 | probs = model.predict_proba(X, W[model.name].values) 407 | probs = np.nan_to_num(probs) 408 | pos_prob_idx = probs.sum(axis=1) > tol 409 | w = probs[pos_prob_idx] 410 | c = scattercolors(w, samplefn=samplefn) 411 | 412 | # axloc, axmov = dual_axes() 413 | # field(axloc) 414 | # movement(axmov) 415 | axloc, axpol = loc_angle_axes() 416 | 417 | x = actions[pos_prob_idx] 418 | axloc.scatter(x.x, x.y, c=c, alpha=alpha) 419 | for p, c in zip(w.T, colors): 420 | p = p.flatten() 421 | axpol.hist( 422 | x.mov_angle_a0, weights=p.flatten(), color=c, alpha=alpha, bins=100 423 | ) 424 | # axpol.hist(x.mov_angle_a0, c=c, alpha=alpha) 425 | plt.show() 426 | 427 | 428 | def model_vs_data( 429 | dir_models, loc_models, actions, W, samplefn="max", tol=0.1, figsize=4, alpha=0.5 430 | ): 431 | for loc_model in loc_models: 432 | print(loc_model.name, loc_model.n_components) 433 | for i, gauss in enumerate(loc_model.submodels): 434 | # axloc, axpol = dual_axes() 435 | # # vis.add_ellips(axloc,gauss.mean,gauss.cov) 436 | # draw_contour(axloc, gauss, cmap="Blues") 437 | for dir_model in dir_models: 438 | if f"{loc_model.name}_{i}" == dir_model.name: 439 | 440 | print(dir_model.name, dir_model.n_components) 441 | axcol, axpol = loc_angle_axes() 442 | draw_contour(axcol, gauss, cmap="Blues") 443 | draw_vonmises_pdfs(axpol, dir_model) 444 | plt.show() 445 | 446 | X = actions["mov_angle_a0"] 447 | probs = dir_model.predict_proba(X, W[dir_model.name].values) 448 | probs = np.nan_to_num(probs) 449 | pos_prob_idx = probs.sum(axis=1) > tol 450 | w = probs[pos_prob_idx] 451 | c = scattercolors(w, samplefn=samplefn) 452 | 453 | # axloc, axmov = dual_axes() 454 | # field(axloc) 455 | # movement(axmov) 456 | axloc, axpol = loc_angle_axes() 457 | 458 | x = actions[pos_prob_idx] 459 | axloc.scatter(x.x, x.y, c=c, alpha=alpha) 460 | for p, c in zip(w.T, colors): 461 | p = p.flatten() 462 | axpol.hist( 463 | x.mov_angle_a0, 464 | weights=p.flatten(), 465 | color=c, 466 | alpha=alpha, 467 | bins=100, 468 | ) 469 | # axpol.hist(x.mov_angle_a0, c=c, alpha=alpha) 470 | plt.show() 471 | 472 | 473 | from scipy.spatial import Delaunay 474 | import networkx as nx 475 | 476 | 477 | def color_submodels(means, colors): 478 | tri = Delaunay(means) 479 | edges = set() 480 | for s in tri.simplices: 481 | [a, b, c] = s 482 | es = set([frozenset([a, b]), frozenset([b, c]), frozenset([c, a])]) 483 | edges = edges | es 484 | G = nx.Graph() 485 | for e in edges: 486 | [i, j] = list(e) 487 | G.add_edge(i, j) 488 | 489 | if len(G.nodes) > 0: 490 | r_ = max([G.degree(node) for node in G.nodes]) 491 | else: 492 | r_ = 0 493 | if r_ > len(colors) - 1: 494 | colorassign = nx.algorithms.coloring.greedy_color(G) 495 | else: 496 | colorassign = nx.algorithms.coloring.equitable_color(G, len(colors)) 497 | colorvector = [0] * len(means) 498 | for k, v in colorassign.items(): 499 | colorvector[k] = int(v) 500 | 501 | return [colors[i] for i in colorvector] 502 | 503 | 504 | def sample(probs): 505 | return np.random.choice(len(probs), p=probs / sum(probs)) 506 | 507 | 508 | def scattercolors(weights, colors=colors, samplefn="max"): 509 | if samplefn == "max": 510 | labels = np.argmax(weights, axis=1) 511 | else: 512 | labels = np.apply_along_axis(sample, axis=1, arr=weights) 513 | 514 | pcolors = [colors[l % len(colors)] for l in labels] 515 | return pcolors 516 | 517 | 518 | ################################# 519 | # EXPERIMENTS VISUALIZATION 520 | ################################# 521 | 522 | 523 | def savefigure(figname): 524 | plt.savefig(figname,dpi=300, 525 | bbox_inches="tight", 526 | pad_inches=0.0 527 | ) 528 | 529 | 530 | def show_component_differences(loc_models, dir_models, vec_p1, vec_p2, name1, name2, save=True): 531 | 532 | # determine colors of dir sub models 533 | difference = vec_p1 - vec_p2 534 | cmap = mpl.cm.get_cmap('bwr_r') 535 | 536 | for loc_model in loc_models: 537 | 538 | mini = min(difference.loc[difference.index.str.contains(f"^{loc_model.name}_")]) 539 | maxi = max(difference.loc[difference.index.str.contains(f"^{loc_model.name}_")]) 540 | ab = max(abs(mini), abs(maxi)) 541 | 542 | if (ab == 0): 543 | ab = 0.0001 544 | 545 | norm = mpl.colors.DivergingNorm(vcenter=0, vmin=-ab, 546 | vmax = ab) 547 | 548 | print(loc_model.name, loc_model.n_components) 549 | ax = mps.field(show=False, figsize=8) 550 | 551 | 552 | am_subclusters = [] 553 | for a, _ in enumerate(loc_model.submodels): 554 | for dir_model in dir_models: 555 | if f"{loc_model.name}_{a}" == dir_model.name: 556 | am_subclusters.append(dir_model.n_components) 557 | 558 | am_subclusters = np.array(am_subclusters) 559 | 560 | for i, gauss in enumerate(loc_model.submodels): 561 | 562 | if (am_subclusters == 1).all(): 563 | add_ellips(ax, gauss.mean, gauss.cov, 564 | color=cmap(norm(difference.loc[f"{loc_model.name}_{i}_0"])), alpha=1) 565 | 566 | else: 567 | add_ellips(ax, gauss.mean, gauss.cov, color='gainsboro') 568 | 569 | x, y = gauss.mean 570 | for dir_model in dir_models: 571 | if f"{loc_model.name}_{i}" == dir_model.name: 572 | print(dir_model.name, dir_model.n_components) 573 | 574 | for j, vonmises in enumerate(dir_model.submodels): 575 | dx = np.cos(vonmises.loc)[0] 576 | dy = np.sin(vonmises.loc)[0] 577 | add_arrow(ax, x, y, 10*dx, 10*dy, 578 | fc=cmap(norm(difference.loc[f"{loc_model.name}_{i}_{j}"])), 579 | arrowsize=4.5, linewidth=1 580 | ) 581 | 582 | cb = plt.colorbar(plt.cm.ScalarMappable(cmap=cmap, norm=norm), ax=ax, fraction=0.065, pad=-0.05, orientation='horizontal') 583 | cb.ax.xaxis.set_ticks_position('bottom') 584 | cb.ax.tick_params(labelsize=16) 585 | plt.axis("scaled") 586 | 587 | if save: 588 | savefigure(f"../figures/{name1}-{name2}-{loc_model.name}.png") 589 | else: 590 | plt.show() 591 | --------------------------------------------------------------------------------