{ "cells": [ { "cell_type": "markdown", "source": [ "# Preprocessing time series with aeon\n", "\n", "It is common to need to preprocess time series data before applying machine learning\n", "algorithms. So algorithms can handle these characteristics, or `aeon` transformers can be used to preprocess collections of time\n", "series into standard format. This notebook demonstrates three common use cases\n", "\n", "1. [Rescaling time series](#Rescaling-time-series)\n", "2. [Resizing time series](#Resizing-time-series)\n", "3. [Dealing with missing values](#missing-values)\n" ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "## Rescaling time series\n", "\n", "Different levels of scale and variance can mask discriminative patterns in time\n", "series. This is particularly true for methods that are based on distances. It common\n", "to rescale time series to have zero mean and unit variance. For example, the data in\n", "the `UnitTest` dataset is a subset of the [Chinatown dataset]\n", "(https://timeseriesclassification.com/description.php?Dataset=Chinatown. These are\n", "counts of pedestrians in Chinatown, Melbourne. The time series are of different means" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "source": [ "import numpy as np\n", "\n", "from aeon.datasets import load_unit_test\n", "\n", "X, y = load_unit_test(split=\"Train\")\n", "np.mean(X, axis=-1)[0:5]" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2024-11-17T13:48:57.655001Z", "start_time": "2024-11-17T13:48:57.631756Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[561.875 ],\n", " [604.95833333],\n", " [629.16666667],\n", " [801.45833333],\n", " [540.75 ]])" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 3 }, { "cell_type": "code", "source": [ "np.std(X, axis=-1)[0:5]" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2024-11-17T13:48:59.467239Z", "start_time": "2024-11-17T13:48:59.458263Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[428.95224215],\n", " [483.35481095],\n", " [514.90052977],\n", " [629.00847763],\n", " [389.10059218]])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 4 }, { "cell_type": "markdown", "source": [ "We can rescale the time series in three ways:\n", "1. Normalise: subtract the mean and divide by the standard deviation to make all\n", "series have zero mean and unit variance." ], "metadata": { "collapsed": false } }, { "cell_type": "code", "source": [ "from aeon.transformations.collection import Normalizer\n", "\n", "normalizer = Normalizer()\n", "X2 = normalizer.fit_transform(X)\n", "np.round(np.mean(X2, axis=-1)[0:5], 6)" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2024-11-17T13:49:01.643630Z", "start_time": "2024-11-17T13:49:01.627083Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.],\n", " [-0.],\n", " [ 0.],\n", " [-0.],\n", " [-0.]])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 5 }, { "cell_type": "code", "source": [ "np.round(np.std(X2, axis=-1)[0:5], 6)" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2024-11-17T13:49:02.670358Z", "start_time": "2024-11-17T13:49:02.648594Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[1.],\n", " [1.],\n", " [1.],\n", " [1.],\n", " [1.]])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 6 }, { "cell_type": "markdown", "source": [ "2. Re-center: Recentering involves subtracting the mean of each series" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "source": [ "from aeon.transformations.collection import Centerer\n", "\n", "c = Centerer()\n", "X3 = c.fit_transform(X)\n", "np.round(np.mean(X3, axis=-1)[0:5], 6)" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2024-11-17T13:49:04.345033Z", "start_time": "2024-11-17T13:49:04.332065Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[ 0.],\n", " [-0.],\n", " [ 0.],\n", " [-0.],\n", " [ 0.]])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 7 }, { "cell_type": "markdown", "source": [ "3. Min-Max: Scale the data to be between 0 and 1" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "source": [ "from aeon.transformations.collection import MinMaxScaler\n", "\n", "minmax = MinMaxScaler()\n", "X4 = minmax.fit_transform(X)\n", "np.round(np.min(X4, axis=-1)[0:5], 6)" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2024-11-17T13:49:06.135780Z", "start_time": "2024-11-17T13:49:06.116831Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[0.],\n", " [0.],\n", " [0.],\n", " [0.],\n", " [0.]])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 8 }, { "cell_type": "code", "source": [ "np.round(np.max(X4, axis=-1)[0:5], 6)" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2024-11-17T13:49:07.094710Z", "start_time": "2024-11-17T13:49:07.072733Z" } }, "outputs": [ { "data": { "text/plain": [ "array([[1.],\n", " [1.],\n", " [1.],\n", " [1.],\n", " [1.]])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 9 }, { "cell_type": "markdown", "source": [ "There is no best way to do this, although for counts such as this it is more common\n", "to MinMax scale, so that the data still has some interpretation as proportions." ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Resizing time series\n", "\n", "Suppose we have a collections of time series with different lengths, i.e. different\n", "number of time points. Currently, most of aeon's collection estimators\n", "(classification, clustering or regression) require equal-length time\n", "series. Those that can handle unequal length series are tagged with\n", "\"capability:unequal\"." ] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2020-12-19T14:31:58.456171Z", "iopub.status.busy": "2020-12-19T14:31:58.455565Z", "iopub.status.idle": "2020-12-19T14:31:59.189497Z", "shell.execute_reply": "2020-12-19T14:31:59.190005Z" }, "pycharm": { "is_executing": true }, "ExecuteTime": { "end_time": "2024-11-17T13:49:10.589292Z", "start_time": "2024-11-17T13:49:10.576923Z" } }, "source": [ "from aeon.classification.convolution_based import RocketClassifier\n", "from aeon.datasets import load_basic_motions, load_japanese_vowels, load_plaid\n", "from aeon.utils.validation import has_missing, is_equal_length, is_univariate" ], "outputs": [], "execution_count": 10 }, { "cell_type": "markdown", "source": [ "If you want to use an estimator that cannot internally handle missing values, one\n", "option is to convert unequal length series into equal length. This can be\n", " done through padding, truncation or resizing through fitting a function and\n", " resampling." ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Unequal or equal length collections time series\n", "\n", "If a collection contains all equal length series, it will store the data in a 3D\n", "numpy of shape `(n_cases, n_channels, n_timepoints)`. If it is unequal length, it is\n", "stored in a list of 2D numpy arrays:" ] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2020-12-19T14:31:59.194445Z", "iopub.status.busy": "2020-12-19T14:31:59.193903Z", "iopub.status.idle": "2020-12-19T14:32:01.019896Z", "shell.execute_reply": "2020-12-19T14:32:01.020463Z" }, "pycharm": { "is_executing": true }, "ExecuteTime": { "end_time": "2024-11-17T13:49:17.636972Z", "start_time": "2024-11-17T13:49:17.599882Z" } }, "source": [ "# Equal length multivariate data\n", "bm_X, bm_y = load_basic_motions()\n", "X = bm_X\n", "print(f\"{type(X)}, {X.shape}\")\n", "print(\n", " f\"univariate = {is_univariate(X)}, has missing ={has_missing(X)}, equal \"\n", " f\"length = {is_equal_length(X)}\"\n", ")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ", (80, 6, 100)\n", "univariate = False, has missing =False, equal length = True\n" ] } ], "execution_count": 11 }, { "cell_type": "code", "source": [ "# Unequal length univariate data\n", "plaid_X, plaid_y = load_plaid()\n", "X = plaid_X\n", "print(type(plaid_X), \"\\n\", plaid_X[0].shape, \"\\n\", plaid_X[10].shape)\n", "print(\n", " f\"univariate = {is_univariate(X)}, has missing ={has_missing(X)}, equal \"\n", " f\"length = {is_equal_length(X)}\"\n", ")" ], "metadata": { "collapsed": false, "pycharm": { "is_executing": true }, "ExecuteTime": { "end_time": "2024-11-17T13:49:18.797171Z", "start_time": "2024-11-17T13:49:18.626506Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " \n", " (1, 500) \n", " (1, 300)\n", "univariate = True, has missing =False, equal length = False\n" ] } ], "execution_count": 12 }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:49:19.575389Z", "start_time": "2024-11-17T13:49:19.526275Z" } }, "cell_type": "code", "source": [ "vowels_X, vowels_y = load_japanese_vowels(split=\"train\")\n", "X = vowels_X\n", "print(\n", " f\"univariate = {is_univariate(X)}, has missing ={has_missing(X)}, equal \"\n", " f\"length = {is_equal_length(X)}\"\n", ")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "univariate = False, has missing =False, equal length = False\n" ] } ], "execution_count": 13 }, { "cell_type": "markdown", "metadata": {}, "source": "\n" }, { "cell_type": "code", "source": [ "series_lengths = [array.shape[1] for array in plaid_X]\n", "\n", "# Find the minimum and maximum of the second dimensions\n", "min_length = min(series_lengths)\n", "max_length = max(series_lengths)\n", "print(\" Min length = \", min_length, \" max length = \", max_length)" ], "metadata": { "collapsed": false, "pycharm": { "is_executing": true }, "ExecuteTime": { "end_time": "2024-11-17T13:49:21.081698Z", "start_time": "2024-11-17T13:49:21.061750Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Min length = 100 max length = 1344\n" ] } ], "execution_count": 14 }, { "metadata": {}, "cell_type": "markdown", "source": [ "There are two basic strategies for unequal length problems\n", "1. Use an estimator that can internally handle missing values\n", "2. Transform the data to be equal length by, for example, truncating or padding series\n", "\n", "Estimators with the tag `\"capability:unequal_length\": True` have the capability to\n", "handle unequal length series. For classification, regression and\n", "clusterign, the\n", "current list is" ] }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:49:23.280238Z", "start_time": "2024-11-17T13:49:23.143830Z" } }, "cell_type": "code", "source": [ "from aeon.utils.discovery import all_estimators\n", "\n", "all_estimators(\n", " type_filter=[\"classifier\", \"regressor\", \"clusterer\"],\n", " tag_filter={\"capability:unequal_length\": True},\n", ")" ], "outputs": [ { "data": { "text/plain": [ "[('Catch22Classifier',\n", " aeon.classification.feature_based._catch22.Catch22Classifier),\n", " ('Catch22Clusterer', aeon.clustering.feature_based._catch22.Catch22Clusterer),\n", " ('Catch22Regressor', aeon.regression.feature_based._catch22.Catch22Regressor),\n", " ('DummyClassifier', aeon.classification.dummy.DummyClassifier),\n", " ('DummyRegressor', aeon.regression._dummy.DummyRegressor),\n", " ('ElasticEnsemble',\n", " aeon.classification.distance_based._elastic_ensemble.ElasticEnsemble),\n", " ('KNeighborsTimeSeriesClassifier',\n", " aeon.classification.distance_based._time_series_neighbors.KNeighborsTimeSeriesClassifier),\n", " ('KNeighborsTimeSeriesRegressor',\n", " aeon.regression.distance_based._time_series_neighbors.KNeighborsTimeSeriesRegressor),\n", " ('RDSTClassifier', aeon.classification.shapelet_based._rdst.RDSTClassifier),\n", " ('RDSTRegressor', aeon.regression.shapelet_based._rdst.RDSTRegressor)]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 15 }, { "metadata": {}, "cell_type": "markdown", "source": "You can pass these estimators unequal length series and they will work as expected.\n" }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:49:25.499271Z", "start_time": "2024-11-17T13:49:25.466359Z" } }, "cell_type": "code", "source": [ "from aeon.classification.distance_based import KNeighborsTimeSeriesClassifier\n", "\n", "knn = KNeighborsTimeSeriesClassifier()\n", "model = knn.fit(plaid_X, plaid_y)" ], "outputs": [], "execution_count": 16 }, { "metadata": {}, "cell_type": "markdown", "source": [ "If time series are unequal length, collection estimators will raise an error if they\n", "do not have the capability to handle this characteristic. If you want to use them, \n", "you will need to preprocess the data to be equal length. " ] }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:49:27.034532Z", "start_time": "2024-11-17T13:49:27.001467Z" } }, "cell_type": "code", "source": [ "rc = RocketClassifier()\n", "try:\n", " rc.fit(plaid_X, plaid_y)\n", "except ValueError as e:\n", " print(f\"ValueError: {e}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ValueError: Data seen by instance of RocketClassifier has unequal length series, but RocketClassifier cannot handle unequal length series. \n" ] } ], "execution_count": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Padding, truncating or resizing.\n", "\n", "We can pad, truncate or resize. By default, pad adds zeros to make all series the\n", "length of the longest, truncate removes all values beyond the length of the shortest\n", "and resize stretches or shrinks the series." ] }, { "cell_type": "code", "metadata": { "execution": { "iopub.execute_input": "2020-12-19T14:32:01.245270Z", "iopub.status.busy": "2020-12-19T14:32:01.244733Z", "iopub.status.idle": "2020-12-19T14:32:02.911970Z", "shell.execute_reply": "2020-12-19T14:32:02.912833Z" }, "pycharm": { "is_executing": true }, "ExecuteTime": { "end_time": "2024-11-17T13:49:29.582165Z", "start_time": "2024-11-17T13:49:29.437476Z" } }, "source": [ "from aeon.transformations.collection import Padder, Resizer, Truncator\n", "\n", "pad = Padder()\n", "truncate = Truncator()\n", "resize = Resizer(length=600)\n", "X2 = pad.fit_transform(plaid_X)\n", "X3 = truncate.fit_transform(plaid_X)\n", "X4 = resize.fit_transform(plaid_X)\n", "print(X2.shape, \"\\n\", X3.shape, \"\\n\", X4.shape)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1074, 1, 1344) \n", " (1074, 1, 100) \n", " (1074, 1, 600)\n" ] } ], "execution_count": 18 }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:49:36.601769Z", "start_time": "2024-11-17T13:49:35.625172Z" } }, "cell_type": "code", "source": [ "import matplotlib.pyplot as plt\n", "\n", "plt.title(\"Before and after padding: PLAID first case (shifted up for unpadded)\")\n", "plt.plot(plaid_X[0][0] + 10)\n", "plt.plot(X2[0][0])" ], "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "
" ], "image/png": "iVBORw0KGgoAAAANSUhEUgAAAksAAAGzCAYAAAA/lFPrAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABfSklEQVR4nO3deVhU1R8G8HfYBpBNQDYFQTBXXMIl3LdUcs0tzRS31NLULCsrTS2jslLzZ5qVS6aZmmlZ7vuCu2huhIq74AoIyjrn98c0AyMwzuAwh4H38zyjM3fu3Pu9hzv3fuecc89VCCEEiIiIiKhAVrIDICIiIirJmCwRERER6cFkiYiIiEgPJktEREREejBZIiIiItKDyRIRERGRHkyWiIiIiPRgskRERESkB5MlIiIiIj1KTLI0Y8YMVKlSBdbW1qhXr57scMxq586dUCgU2Llzp8mWGRcXh/bt28PV1RUKhQJr16412bJLImPKsFWrVmjVqpX29aVLl6BQKLB48eJii49yHT58GE2aNEG5cuWgUCgQExODKVOmQKFQyA7Noly9ehX29vbYt2+fUZ9TKBQYPXr0E+dbvHgxFAoFLl26pDPdnMfqkrhfFLT/UsEeP9YWpjjOgY/vO1lZWfD398e3335bpOUZlSxpvjx5H15eXmjdujU2bNhQpAAAYPPmzXjnnXfQtGlTLFq0CJ9++mmRl0VqkZGR+OeffzB9+nQsXboUDRo0wPLlyzFr1izZoZEBNF90zcPR0RE1a9bEhx9+iJSUFO18mu/kkSNHDF52o0aNoFAoMG/evALfL2iZBcUTEBCALl26YNGiRcjIyDBo3VlZWejduzfu3buHmTNnYunSpahcubLBsRvqzJkzmDJlSr4TfWkybdo0NG7cGE2bNjXbOgs6Vt+4cQNTpkwpE0mDufZfMj1bW1uMHz8e06dPR3p6utGftynKSqdNm4agoCAIIZCYmIjFixfjhRdewJ9//onOnTsbvbzt27fDysoKP/74I+zs7IoSEuXx6NEjREdH44MPPtD5Bbl8+XKcOnUK48aNkxdcCVS5cmU8evQItra2skPJZ968eXByckJqaio2b96M6dOnY/v27di3b1+RfnHHxcXh8OHDCAwMxLJly/Daa68VKZ6MjAxcv34dmzZtwpAhQzBr1iysX78e/v7+ej9/4cIFXL58Gd9//z2GDRumnf7hhx/ivffeM3p7CnPmzBlMnToVrVq1QmBgoMmWW1Lcvn0bS5YswZIlS4ptHQMGDEDfvn2hVCq10wo6Vh85cgRTp05FYGBgqW8VKGz/JcswePBgvPfee1i+fDmGDBli1GeLlCxFRESgQYMG2tdDhw6Ft7c3fvnllyIlS7du3YKDg4PJEiUhBNLT0+Hg4GCS5Vma27dvAwDc3NyKfV0qlQqZmZmwt7cv9nUVF4VCUWLj79WrFzw9PQEAI0eORM+ePbFmzRocOHAA4eHhRi/v559/hpeXF7766iv06tULly5dMiqZyBsPAEyePBnLli3DwIED0bt3bxw4cEDv52/dugUg/75pY2MDGxv9h6PSsK+Zys8//wwbGxt06dKl2NZhbW0Na2trnWmmPlZbmsL236eRlpaGcuXKmWx5VDg3Nze0b98eixcvNjpZMkmfJTc3Nzg4OOQ72KlUKsyaNQu1atWCvb09vL29MWLECNy/f187j0KhwKJFi5CWlqat4tf0HcnOzsbHH3+M4OBgKJVKBAYG4v33389X5R8YGIjOnTtj06ZNaNCgARwcHPDdd98BAJKSkjBu3Dj4+/tDqVQiJCQEn3/+OVQq1RO3a926dejUqRP8/PygVCoRHByMjz/+GDk5OTrztWrVCrVr18aZM2fQunVrODo6omLFivjiiy/yLfPatWvo3r07ypUrBy8vL7z55psGN2FcvnwZr7/+OqpVqwYHBwd4eHigd+/eOk0NU6ZM0VYLT5gwAQqFAoGBgWjVqhX++usvXL58WVvOeU+SGRkZ+OijjxASEgKlUgl/f3+88847+WLT9HdYtmwZatWqBaVSiY0bNxYas+Zvs3nzZtSrVw/29vaoWbMm1qxZozPfvXv38PbbbyM0NBROTk5wcXFBREQETpw48VRluGDBAgQHB8PBwQGNGjXCnj178s1TUJ+lQYMGwcnJCdevX0f37t3h5OSEChUq4O23387397979y4GDBgAFxcXuLm5ITIyEidOnMi3zKysLJw7dw43b94stLyepE2bNgCA+Pj4In1++fLl6NWrFzp37gxXV1csX768yLFo9O/fH8OGDcPBgwexZcuWQucbNGgQWrZsCQDo3bs3FAqFtj9DQX1T9O1rK1asQFhYGJydneHi4oLQ0FDMnj0bgLoZsXfv3gCA1q1ba/f3J/WHOHfuHPr06YMKFSrAwcEB1apVwwcffKB935DvH6D+O0+dOhVVq1aFvb09PDw80KxZs3xlc+7cOfTq1Qvu7u6wt7dHgwYN8Mcff+iNUWPt2rVo3LgxnJycdKbHxcWhZ8+e8PHxgb29PSpVqoS+ffsiOTm5wGXUrl0bSqUStWrVyvc9frzPUmHH6oYNGwJQ/2p//BgOAAcPHkTHjh3h6uoKR0dHtGzZssB+Vnv37kXDhg1hb2+P4OBg7THcEIGBgRg0aFC+6Y/3mdH0jfn111/x/vvvw8fHB+XKlUPXrl1x9epVvevQt/8C6lq35s2bo1y5cnBzc0O3bt1w9uxZnWVo9vMzZ87g5ZdfRvny5dGsWbNC11lYn62C+pNpjrV79+5Fo0aNYG9vjypVquCnn34q8LO7d+/GiBEj4OHhARcXFwwcOFDn3AwYfg4EDDvWAsYdv4tj33n++eexd+9e3Lt3r9B5ClKkmqXk5GTcuXMHQgjcunULc+bMQWpqKl555RWd+UaMGIHFixdj8ODBGDNmDOLj4/G///0Px48fx759+2Bra4ulS5diwYIFOHToEH744QcAQJMmTQAAw4YNw5IlS9CrVy+89dZbOHjwIKKionD27Fn8/vvvOuuKjY1Fv379MGLECLz66quoVq0aHj58iJYtW+L69esYMWIEAgICsH//fkycOBE3b958Yv+dxYsXw8nJCePHj4eTkxO2b9+OyZMnIyUlBTNmzNCZ9/79++jYsSN69OiBPn36YPXq1Xj33XcRGhqKiIgIAOrmsbZt2+LKlSsYM2YM/Pz8sHTpUmzfvt2gcj98+DD279+Pvn37olKlSrh06RLmzZuHVq1a4cyZM3B0dESPHj3g5uaGN998E/369cMLL7wAJycnlCtXDsnJybh27RpmzpwJANoDrUqlQteuXbF3714MHz4cNWrUwD///IOZM2fi33//zdc5fPv27Vi5ciVGjx4NT0/PJ9ZMxMXF4aWXXsLIkSMRGRmJRYsWoXfv3ti4cSOef/55AMDFixexdu1a9O7dG0FBQUhMTMR3332Hli1b4syZM/Dz8zO6DH/88UeMGDECTZo0wbhx43Dx4kV07doV7u7uT2wuAoCcnBx06NABjRs3xpdffomtW7fiq6++QnBwsLb5SqVSoUuXLjh06BBee+01VK9eHevWrUNkZGS+5V2/fh01atRAZGRkkTuTX7hwAQDg4eFh9GcPHjyI8+fPY9GiRbCzs0OPHj2wbNkyvP/++0WKJa8BAwZgwYIF2Lx5s/Zv+rgRI0agYsWK+PTTTzFmzBg0bNgQ3t7eepdb0L62ZcsW9OvXD23btsXnn38OADh79iz27duHsWPHokWLFhgzZgy++eYbvP/++6hRowYAaP8vyMmTJ9G8eXPY2tpi+PDhCAwMxIULF/Dnn39i+vTpAAz7/gHqE1xUVBSGDRuGRo0aISUlBUeOHMGxY8e0ZXP69Gk0bdoUFStWxHvvvYdy5cph5cqV6N69O3777Te8+OKLhcaalZWFw4cP52tCzczMRIcOHZCRkYE33ngDPj4+uH79OtavX4+kpCS4urpq5927dy/WrFmD119/Hc7Ozvjmm2/Qs2dPXLlypdB9q6BjddWqVTFt2jRMnjwZw4cPR/PmzQHkHsO3b9+OiIgIhIWF4aOPPoKVlRUWLVqENm3aYM+ePWjUqBEA4J9//kH79u1RoUIFTJkyBdnZ2fjoo4+euH8U1fTp06FQKPDuu+/i1q1bmDVrFtq1a4eYmJhCWyT07b9bt25FREQEqlSpgilTpuDRo0eYM2cOmjZtimPHjuU7Rvbu3RtVq1bFp59+CiGEybbr/Pnz6NWrF4YOHYrIyEgsXLgQgwYNQlhYGGrVqqUz7+jRo+Hm5oYpU6YgNjYW8+bNw+XLl7UJJWD4OdDQY60xx+/i2nfCwsIghMD+/fuNawkTRli0aJEAkO+hVCrF4sWLdebds2ePACCWLVumM33jxo35pkdGRopy5crpzBcTEyMAiGHDhulMf/vttwUAsX37du20ypUrCwBi48aNOvN+/PHHoly5cuLff//Vmf7ee+8Ja2trceXKFb3b+/Dhw3zTRowYIRwdHUV6erp2WsuWLQUA8dNPP2mnZWRkCB8fH9GzZ0/ttFmzZgkAYuXKldppaWlpIiQkRAAQO3bsMDqe6OjofOuOj48XAMSMGTN05u3UqZOoXLlyvmUsXbpUWFlZiT179uhMnz9/vgAg9u3bp50GQFhZWYnTp0/rjVVD87f57bfftNOSk5OFr6+vqF+/vnZaenq6yMnJ0flsfHy8UCqVYtq0adpphpZhZmam8PLyEvXq1RMZGRnaeRcsWCAAiJYtW+qsB4BYtGiRdlpkZKQAoLNuIYSoX7++CAsL077+7bffBAAxa9Ys7bScnBzRpk2bfMvUrCcyMlJ/oQkhPvroIwFAxMbGitu3b4v4+Hjx3XffCaVSKby9vUVaWpoQIvc7efjw4Scuc/To0cLf31+oVCohhBCbN28WAMTx48d15itomZp4bt++XeCy79+/LwCIF198UW8MO3bsEADEqlWrCtzevArb18aOHStcXFxEdnZ2oetZtWqVQd8pjRYtWghnZ2dx+fJlnemashLC8O9f3bp1RadOnfSur23btiI0NFTnOKJSqUSTJk1E1apV9X72/PnzAoCYM2eOzvTjx48XWLaPAyDs7OzE+fPntdNOnDiRb5ma/SA+Pl47raBj9eHDh/Pt65rtqVq1qujQoUO+cgwKChLPP/+8dlr37t2Fvb29TvmfOXNGWFtb59svClK5cuUCv1ctW7bU+a5r9r+KFSuKlJQU7fSVK1cKAGL27Nl611PY/luvXj3h5eUl7t69q5124sQJYWVlJQYOHKidptnP+/Xr98Rtyjv/4wr622iOtbt379ZOu3XrllAqleKtt97K99mwsDCRmZmpnf7FF18IAGLdunXaaYacA4051hp6/C7OfefGjRsCgPj888/zvadPkZrh5s6diy1btmDLli34+eef0bp1awwbNkynaWXVqlVwdXXF888/jzt37mgfYWFhcHJywo4dO/Su4++//wYAjB8/Xmf6W2+9BQD466+/dKYHBQWhQ4cOOtNWrVqF5s2bo3z58joxtGvXDjk5Odi9e7feGPL+wnjw4AHu3LmD5s2b4+HDhzh37pzOvE5OTjo1a3Z2dmjUqBEuXryos02+vr7o1auXdpqjoyOGDx+uN46C4snKysLdu3cREhICNzc3HDt2zKBlFGTVqlWoUaMGqlevrlNOmiafx/9WLVu2RM2aNQ1evp+fn84vZU2V7/Hjx5GQkAAAUCqVsLJS7445OTm4e/cunJycUK1aNZ1tM7QMjxw5glu3bmHkyJE6/SsGDRqk8wv7SUaOHKnzunnz5jp/040bN8LW1havvvqqdpqVlRVGjRqVb1mBgYEQQhhVq1StWjVUqFABQUFBGDFiBEJCQvDXX39pazEMlZ2djV9//RUvvfSS9ldjmzZt4OXlhWXLlhm1rIJoaikfPHjw1MvKq6B9zc3NDWlpaXqb/Ixx+/Zt7N69G0OGDEFAQIDOe3mbQAz9/rm5ueH06dOIi4srcH337t3D9u3b0adPH+1x5c6dO7h79y46dOiAuLg4XL9+vdB47969CwAoX768znTNfr1p0yY8fPhQ7za3a9cOwcHB2td16tSBi4uLzr79tGJiYhAXF4eXX34Zd+/e1W5nWloa2rZti927d0OlUiEnJwebNm1C9+7ddcq/Ro0a+Y7ppjJw4EA4OztrX/fq1Qu+vr7a844xbt68iZiYGAwaNAju7u7a6XXq1MHzzz9f4DIfP66YSs2aNbW1ewBQoUIFVKtWrcC/6/Dhw3UuannttddgY2OjE68h50BjjrWGHr+Lc9/RfG/u3LlT4PuFKVIzXKNGjXQ6ePfr1w/169fH6NGj0blzZ9jZ2SEuLg7Jycnw8vIqcBmajnKFuXz5MqysrBASEqIz3cfHB25ubrh8+bLO9KCgoHzLiIuLw8mTJ1GhQoUixXD69Gl8+OGH2L59u87l2gDy9QGoVKlSvrbl8uXL4+TJkzrbFBISkm++atWq6Y1D49GjR4iKisKiRYtw/fp1nerbgvokGCouLg5nz541uJwKKmt9CtrmZ555BoC6v5CPjw9UKhVmz56Nb7/9FvHx8Tpt4nmbBQwtQ83+UbVqVZ3ptra2qFKlikFx29vb5yuT8uXL67TrX758Gb6+vvmSl8f326L67bff4OLiAltbW1SqVEnnBGeMzZs34/bt22jUqBHOnz+vnd66dWv88ssv+Pzzz7XJalGkpqYCgM4JyBQK2tdef/11rFy5EhEREahYsSLat2+PPn36oGPHjkVah+ZEUrt2bb3zGfr9mzZtGrp164ZnnnkGtWvXRseOHTFgwADUqVMHgLqpRAiBSZMmYdKkSQWu69atW6hYsaLeePKuH1CX1fjx4/H1119j2bJlaN68Obp27YpXXnkl30nr8aQQyL9vPy1NslhQk7RGcnIyMjIy8OjRo3zfVUD9vS5KAvMkj69LoVAgJCSkSENNaI41BR3Ha9SogU2bNuXrxG3sMdRQxvxdHy8DJycn+Pr66pSBIedAY461hh6/i3Pf0XxvjL2auEjJ0uOsrKzQunVrzJ49G3FxcahVqxZUKpXeX62FnZgfZ+gGFdTOrFKp8Pzzz+Odd94p8DOaE3ZBkpKS0LJlS7i4uGDatGkIDg6Gvb09jh07hnfffTdfB/HHrxrRePyA9jTeeOMNLFq0COPGjUN4eLh2wMm+ffsa1GG9MCqVCqGhofj6668LfP/x/j3FcZXhp59+ikmTJmHIkCH4+OOP4e7uDisrK4wbN+6ptu1pFPY3NacWLVroXH1WVJrvYZ8+fQp8f9euXWjdunWRl3/q1CkApksSNQra17y8vBATE4NNmzZhw4YN2LBhAxYtWoSBAwcW66X0hn7/WrRogQsXLmDdunXYvHkzfvjhB8ycORPz58/HsGHDtPO+/fbbhf761VeOmh8PBZ0Av/rqKwwaNEi77jFjxiAqKgoHDhxApUqVtPOZ43il2c4ZM2YUOqSAZhiKp1XYeSInJ6dEfI8fZ+gxVN92FcSUf1djz4GmVJz7juZ7Y+xx1STJEqCu5gdyf2EGBwdj69ataNq0aZFOrpUrV4ZKpUJcXJxOx8zExEQkJSUZNBBYcHAwUlNT0a5dO6PXv3PnTty9exdr1qxBixYttNOLehUSoN6mU6dOQQih8yWIjY016POrV69GZGQkvvrqK+209PR0JCUlGfT5wr54wcHBOHHiBNq2bVsso+VqfknnXfa///4LANqOj6tXr0br1q3x448/6nw2KSlJZ6c2tAw1+0dcXJy2ORFQN5/Ex8ejbt26Jtm2ypUrY8eOHXj48KFO7VLe2hvZ0tLSsG7dOrz00ks61d8aY8aMwbJly54qWVq6dCkAFFuzyePs7OzQpUsXdOnSBSqVCq+//jq+++47TJo0qcBfrvpofv1qEr7CGPP9c3d3x+DBgzF48GCkpqaiRYsWmDJlCoYNG6Zdn62tbZGOTQEBAXBwcCj0WBQaGorQ0FB8+OGH2L9/P5o2bYr58+fjk08+MXpdhtB3XAHUze76tlNz9WFBzZaGHhvLly9f4N/h8uXLBdYkP74uIQTOnz+vrf0zhuZYU1Cs586dg6enZ5GHBtA0GSUlJekMV/B4y0pRxMXF6XznU1NTcfPmTbzwwgsADD8HGnOsNfT4XZz7jiZ+fRd8FMQkQwdkZWVh8+bNsLOz0wbQp08f5OTk4OOPP843f3Z29hNP8Jo/2ONXrGlqPzp16vTEuPr06YPo6Ghs2rQp33tJSUnaBK8gmgw9b0aemZlZ5KHSAfU23bhxA6tXr9ZOe/jwIRYsWGDQ562trfP9QpgzZ06hvzIep7ki7nF9+vTB9evX8f333+d779GjR0hLSzNo+YW5ceOGztWLKSkp+Omnn1CvXj34+PgAKHjbVq1ala/vhqFl2KBBA1SoUAHz589HZmamdvrixYsNTi4N0aFDB2RlZemUnUqlwty5c/PNa4qhA4ri999/R1paGkaNGoVevXrle3Tu3Bm//fZbkX/hL1++HD/88APCw8PRtm1bE0efn6bPjoaVlZX2JKfZBs3JyZC/dYUKFdCiRQssXLgQV65c0Xkv7z5p6Pfv8ficnJwQEhKijc3LywutWrXCd999V+C+oBknrTC2trZo0KBBvlHbU1JS8h3TQkNDYWVlZZLam8IUVtZhYWEIDg7Gl19+qf0RnZdmO62trdGhQwesXbtWp/zPnj1b4LG7IMHBwThw4IDOd339+vWFDgfw008/6fSvW716NW7evKm9ctkYvr6+qFevHpYsWaJTBqdOncLmzZu157Ki0CQNefvXpqWlmaQGdcGCBcjKytK+njdvHrKzs7VlYOg50JhjraHH7+Lcd44ePQqFQmH0OHVFqlnasGGDtnPXrVu3sHz5csTFxeG9996Di4sLAHXHzBEjRiAqKgoxMTFo3749bG1tERcXh1WrVmH27NkF/srVqFu3LiIjI7FgwQJtdeChQ4ewZMkSdO/e3aBfwRMmTMAff/yBzp07ay+fTEtLwz///IPVq1fj0qVLhVbFNWnSBOXLl0dkZCTGjBkDhUKBpUuXPlU19auvvor//e9/GDhwII4ePQpfX18sXbrU4M66nTt3xtKlS+Hq6oqaNWsiOjoaW7duNfgy8rCwMPz6668YP348GjZsCCcnJ3Tp0gUDBgzAypUrMXLkSOzYsQNNmzZFTk4Ozp07h5UrV2rHryqqZ555BkOHDsXhw4fh7e2NhQsXIjExEYsWLdLZtmnTpmHw4MFo0qQJ/vnnHyxbtizfr0JDy9DW1haffPIJRowYgTZt2uCll15CfHw8Fi1aZHCfJUN0794djRo1wltvvYXz58+jevXq+OOPP7RjeOT99WSKoQMKs3DhwgLHuxo7diyWLVsGDw8P7eXcj+vatSu+//57/PXXX+jRo4fe9axevRpOTk7IzMzUjuC9b98+1K1bF6tWrTLJtjzJsGHDcO/ePbRp0waVKlXC5cuXMWfOHNSrV0/7Y61evXqwtrbG559/juTkZCiVSm2H9oJ88803aNasGZ599lkMHz4cQUFBuHTpEv766y/tbTwM/f7VrFkTrVq1QlhYGNzd3XHkyBGsXr1aZzT9uXPnolmzZggNDcWrr76KKlWqIDExEdHR0bh27VqB44vl1a1bN3zwwQdISUnRHnO3b9+O0aNHo3fv3njmmWeQnZ2NpUuXwtraGj179ixqcT9RcHAw3NzcMH/+fDg7O6NcuXJo3LgxgoKC8MMPPyAiIgK1atXC4MGDUbFiRVy/fh07duyAi4sL/vzzTwDA1KlTsXHjRjRv3hyvv/46srOzMWfOHNSqVUun32dhhg0bhtWrV6Njx47o06cPLly4gJ9//rnQPn7u7u5o1qwZBg8ejMTERMyaNQshISE6F2oYY8aMGYiIiEB4eDiGDh2qHTrA1dUVU6ZMKdIyAaB9+/YICAjA0KFDMWHCBFhbW2PhwoWoUKFCvsTeWJmZmWjbti369OmD2NhYfPvtt2jWrBm6du0KwPBzoDHHWkOP31ZWVsW272zZsgVNmzY1fvgVYy6dK2joAHt7e1GvXj0xb948nUv8NBYsWCDCwsKEg4ODcHZ2FqGhoeKdd94RN27c0M5T0OWoQgiRlZUlpk6dKoKCgoStra3w9/cXEydO1LncVgj1JZOFXar74MEDMXHiRBESEiLs7OyEp6enaNKkifjyyy91LpssyL59+8Rzzz0nHBwchJ+fn3jnnXfEpk2b8l2S3LJlS1GrVq18n4+MjMx3qf7ly5dF165dhaOjo/D09BRjx47VDqfwpMuc79+/LwYPHiw8PT2Fk5OT6NChgzh37ly+y2YLGzogNTVVvPzyy8LNzU0A0IktMzNTfP7556JWrVpCqVSK8uXLi7CwMDF16lSRnJysnQ+AGDVqlN4489L8bTZt2iTq1KkjlEqlqF69er5Lb9PT08Vbb70lfH19hYODg2jatKmIjo7Od+mvsWX47bffiqCgIKFUKkWDBg3E7t278y2zsKEDCtonC7qU9/bt2+Lll18Wzs7OwtXVVQwaNEjs27dPABArVqzItx5jhg4o7FJ9jcKG89A8Ll++LGxsbMSAAQMKXcbDhw+Fo6Oj9rJ/fUMH5P3eV6pUSXTu3FksXLgw33eyMMYOHVDQvrZ69WrRvn174eXlJezs7ERAQIAYMWKEuHnzps5833//vahSpYr2EuInfb9OnTolXnzxReHm5ibs7e1FtWrVxKRJk7TvG/r9++STT0SjRo2Em5ubcHBwENWrVxfTp0/Pd7y5cOGCGDhwoPDx8RG2traiYsWKonPnzmL16tV64xRCiMTERGFjYyOWLl2qnXbx4kUxZMgQERwcLOzt7YW7u7to3bq12Lp1q85nCyvXx7fD0KEDhBBi3bp1ombNmsLGxibfd+n48eOiR48ewsPDQyiVSlG5cmXRp08fsW3bNp1l7Nq1S4SFhQk7OztRpUoVMX/+/EIvnS/IV199JSpWrCiUSqVo2rSpOHLkSKFDB/zyyy9i4sSJwsvLSzg4OIhOnTrlGzaiIIXtv0IIsXXrVtG0aVPh4OAgXFxcRJcuXcSZM2d05jH0e53X0aNHRePGjbX7+tdff13o0AEFnQcfLwPNZ3ft2iWGDx8uypcvL5ycnET//v11hj4QwvBzoBCGHWuFMO74bep9JykpSdjZ2YkffvhBf6EXQCGECXv0ET0mMDAQtWvXxvr162WHYlZr167Fiy++iL1795r1RqdUdgwdOhT//vtvoSMlU347d+5E69atsWrVKr0tG6WZZqDow4cPP1WLgSWaNWsWvvjiC1y4cMHovtQm6bNEVJY9evRI53VOTg7mzJkDFxcXPPvss5KiotLuo48+wuHDhwu8/QMR6crKysLXX3+NDz/8sEgXnZnsajiisuqNN97Ao0ePEB4ejoyMDKxZswb79+/Hp59+WmZv5kzFLyAgAOnp6bLDILIItra2T9XPi8kS0VNq06YNvvrqK6xfvx7p6ekICQnBnDlzdDr0EhGR5WKfJSIiIiI92GeJiIiISA8mS0RERER6WGSfJZVKhRs3bsDZ2blYbs9BREREpieEwIMHD+Dn5/dUN+82N4tMlm7cuJHv5q5ERERkGa5evapzc+eSziKTJWdnZwDqwtYM9U9EREQlW0pKCvz9/bXncUthkcmSpunNxcWFyRIREZGFsbQuNJbTYEhEREQkAZMlIiIiIj2YLBERERHpwWSJiIiISA8mS0RERER6MFkiIiIi0oPJEhEREZEeTJaIiIiI9GCyRERERKQHkyUiIiIiPZgsEREREenBZImIiIhIDyZLkpy6noyFe+ORoxKyQyEiIiI9bGQHUFZ1nrMXALDq6DWMbh2CTnV8JUdEREREBWHNkmRnb6Zg1PJj+GpzLC7eTpUdDhERET2GNUslxJzt5zFn+3nM6FUHq45cg6PSGk2CPdC+pg8CPcvJDo+IiKjMUgghLK7TTEpKClxdXZGcnAwXFxfZ4RRJ4Ht/GTzvN/3q44XaPrCxZkUgERFZLks9f/PsawHG/HIcA348hC1nEmWHQkREVOYwWbIQ0Rfv4tWfjuD2gwwIIZD8MEt2SERERGUC+yxZmIbTt2qfLx3aCM2rVpAYDRERUenHmiULNuDHQ4jacFZ2GERERKUakyUL992uizh25T4HtyQiIiomTJZKgR7f7seQxYdlh0FERFQqMVkqJXb9exud5+zBBQ5sSUREZFJMlkqRU9dT0ParXZjyx2lY4PBZREREJRKvhiuFFu+/hORHWRjaLAi1K7rKDoeIiMiiMVkqpX4/fh2/H7+Oim4OmNv/WdTzd5MdEhERkUViM1wpdz3pEbrP3YdvtsXJDoWIiMgisWapjPh6y7+4cu8hBjcNRC0/Ns0REREZijVLEsjqfL366DV0+mYvfthzkbdLISIiMpBJk6V58+ahTp06cHFxgYuLC8LDw7Fhwwbt++np6Rg1ahQ8PDzg5OSEnj17IjGx7N0cVvYAkp/8dRZvroyRGgMREZGlMGmyVKlSJXz22Wc4evQojhw5gjZt2qBbt244ffo0AODNN9/En3/+iVWrVmHXrl24ceMGevToYcoQLEJOCbisf/u5Wxi48BAys1WyQyEiIirRFKKY24Tc3d0xY8YM9OrVCxUqVMDy5cvRq1cvAMC5c+dQo0YNREdH47nnnit0GRkZGcjIyNC+TklJgb+/P5KTk+Hi4lKc4ReLh5nZqDl5k+wwAAAzX6qLF+tXkh0GERGVASkpKXB1dbW483ex9VnKycnBihUrkJaWhvDwcBw9ehRZWVlo166ddp7q1asjICAA0dHRepcVFRUFV1dX7cPf37+4wjYL2c1web356wn8fOCy7DCIiIhKLJMnS//88w+cnJygVCoxcuRI/P7776hZsyYSEhJgZ2cHNzc3nfm9vb2RkJCgd5kTJ05EcnKy9nH16lVTh21WqhLW8vXh2lM4eS0J2TklLDAiIqISwORDB1SrVg0xMTFITk7G6tWrERkZiV27dj3VMpVKJZRKpYkilK8k9Fl6XNf/7cOQpkGY3KWm7FCIiIhKFJPXLNnZ2SEkJARhYWGIiopC3bp1MXv2bPj4+CAzMxNJSUk68ycmJsLHx8fUYZRoJakZLq+F++KRlpEtOwwiIqISpdjHWVKpVMjIyEBYWBhsbW2xbds27XuxsbG4cuUKwsPDizuMEqWkJksAUH/aFly991B2GERERCWGSZvhJk6ciIiICAQEBODBgwdYvnw5du7ciU2bNsHV1RVDhw7F+PHj4e7uDhcXF7zxxhsIDw/XeyVcaVQSm+E0MnNUaP7FDiwe3BCtqnnJDoeIiEg6kyZLt27dwsCBA3Hz5k24urqiTp062LRpE55//nkAwMyZM2FlZYWePXsiIyMDHTp0wLfffmvKECyCqgTXLGkMWnQYxyY9D/dydrJDISIikqrYx1kqDpY6ToPGpTtpaPXlTtlhPFGzEE982LkGqvtYXhkTEVHJY6nnb94bToKS3AyX197zd9Bx1h7M2HQON5IeyQ6HiIhICiZLEpTkDt4FmbvjAr7cHCs7DCIiIimYLElgackSAKw5dh0Rs/cgJT1LdihERERmxWRJAktMlgDg7M0UTFl3Gkcv35cdChERkdkwWZJAZSF9lgqy5vh1DP/pCB5l5sgOhYiIyCyYLElgqTVLGnfTMlF7yiYs2X9JdihERETFjsmSBJaeLAHqbfhycywSktNlh0JERFSsTH4jXXqy0pAsAcCD9Gw8F7UNEbV90CjIHYObBskOiYiIyORYsySBpYyzZKgNpxIw9c8zssMgIiIqFkyWJFCpZEdQPD5ZfwbnElJkh0FERGRSTJYkKG01Sxo/7I1H5MJDssMgIiIyKfZZksASbqRbVIkpGWg1YwcaBLqjSoVyeL1ViOyQiIiIngqTJQmyS3GyBACX7j7EpbsPAQBCAB1r+yC4gpPkqIiIiIqGzXASlJar4QwxY1Ms2n61C73n78fNZN6Ml4iILA+TJQkseQTvojp86T4+23AOogxuOxERWTYmSxKUpZqlvNbF3MBL3x3Ag/QsJk1ERGQxmCxJUBZrljQOXbqH0Cmb8dEfp2WHQkREZBAmSxJk55TdZEnjp+jLGPPLcdx6wNulEBFRycZkSYLSOs6Ssf44cQONpm/Dm7/GsFmOiIhKLCZLEpTmcZaK4vfj13HhdqrsMIiIiArEZEkC1izl1+7r3fj077OywyAiIsqHg1JKwJqlgi3YfREAMLJlMNzL2UmOhoiISI3JkgSlfQTvp7Fg90Us2H0RYZXLw9ZagRbPVEBFNwe0r+kDBztr2eEREVEZxGRJgrI6zpIxjl6+DwA4cPEeAODlxgH49MVQmSEREVEZxT5LEpTlcZaKavnBKxi25DDSs3Kw+9/baP3lThyKvyc7LCIiKgOYLEmQo5IdgWXaevYWqk/aiIELDyH+ThoG/HhQ+16OSuDU9WTW2hERkckxWZKANUumkZGtQre5+5D8KAufbTiLznP24uP1ZwAAO2Jv4VxCis78f5y4gegLdwGoO9knP8wye8xERGR52GeJLNqJq0moO3Wz9vXi/ZfQroY3Bi86DAAIr+KB11oFIzNbhTG/HAcAXPqsE0b8fBRbziTirzHNUMvPNd9yM7JzsObYdTSv6olK5R0xa+u/yMxW4Z2O1Y2O8eLtVNx+kIHGVTyKuJVERCQTkyUJOFp18XolT/Nc9MW7iL54V+f9hOR0bDmTCACIXHgYkzrXwOYziXB1sMX5W6n4qnddLDt4BfN3XYCjnTUOf9AOs7bGAQAGNw3C2Zsp+HFvPD7tEYqKbg751i+EwOqj11DP3w1VvZ3R5qtdAICt41vASqHAD3vj8VrLYPi7OxZXERARkQkphAWeuVNSUuDq6ork5GS4uLjIDsdoc7bF4ast/8oOgwzUqY4v/jp5EwBQu6ILTl3Pbd6LqO2DO6kZcLCzwaU7aRjbtiqOX72Pnw9cAaCuxQp87y8AwJx+9TH9r7NISElHiJcTNo5tjrdXnUCDQHe88lxl3EpJx9X7DxFW2d38G0lEZAaWev5mzRLRE2gSJQA6iRIAbDiVoPP6rVUndF63/nKn9vmiffFISFHfOPj8rVT8fSoBa2NuYG3MDbzyXGU0+nQbAOC315qggpMSMzbHol0NL2Rmq2BnY4UKTko0CfE05aYREZEBmCxJYHFVeVRk8XfStM+PXUnSeU/ThwoA5u28oH3ec95+7fM/T9zQ+cylzzqZOEIiInoSXg1HVAJ8vvGc7BCIiKgQTJYksLxeYlRScBwpIiLzY7JEZEEyszmiKRGRuTFZkkCw1xIVEZMlIiLzY7JUAnk5K2WHQCVURk6O7BCIiMocXg0ngabPUv0AN3Sp44eXGvojNSMbc7bHoV+jAFT3cUGPefthrQAaV/HA+pM3cPXeI7lBU4mQkcWaJSIic2OyJIGmEa62nyuGNAsCAJRT2uCT7qHaeda+3gQAoFAo8Hb7arj9IAOfbTiLtTE3Hl8clSGZvAszEZHZsRmuhFIoFFAoFAAAaysFfFztMatvfbzRJkRyZCQT+ywREZkfa5Zk+K8d7r9cyCijWofA2d4GVTydcPFOKj79m+PzlCVMloiIzM+kNUtRUVFo2LAhnJ2d4eXlhe7duyM2NlZnnlatWmlrTTSPkSNHmjIMi1GEXAn2ttYY3iIY7Wp6Y3iLYOx7rw1m961n6tCohMpgskREZHYmTZZ27dqFUaNG4cCBA9iyZQuysrLQvn17pKWl6cz36quv4ubNm9rHF198YcowSjxTDhxQ0c0B3epVxInJ7U24VCqpWLNERGR+Jm2G27hxo87rxYsXw8vLC0ePHkWLFi200x0dHeHj42PKVVskRVHa4Qrh6miLNa83wU/7LyEhJR0HLt4z2bKp5Mjk0AFERGZXrH2WkpOTAQDu7u4605ctW4aff/4ZPj4+6NKlCyZNmgRHR8dCl5ORkYGMjAzt65SUlELntQTFdbuTZwPK49mA8gCAlPQsjFx6FPsv3C2elZEUvBiOiMj8ii1ZUqlUGDduHJo2bYratWtrp7/88suoXLky/Pz8cPLkSbz77ruIjY3FmjVrCl1WVFQUpk6dWlyhlkou9rZY/upzuJWSjg2nEnAuIQW/HLoqOyx6Srw3HBGR+RVbsjRq1CicOnUKe/fu1Zk+fPhw7fPQ0FD4+vqibdu2uHDhAoKDgwtc1sSJEzF+/Hjt65SUFPj7+xdP4GZgztudeLnYI7JJIADg0xdD8ceJGxi7IsZs6yfTUvEuzEREZlcsydLo0aOxfv167N69G5UqVdI7b+PGjQEA58+fLzRZUiqVUCpLzy1ANOc7E3ZZMohCoUC3ehWRoxJ497eTyMrhidfSsGaJiMj8TJosCSHwxhtv4Pfff8fOnTsRFBT0xM/ExMQAAHx9fU0ZCunR49lK6FrXD7ceZCBqwzn8eYKjglsK1iwREZmfSZOlUaNGYfny5Vi3bh2cnZ2RkJAAAHB1dYWDgwMuXLiA5cuX44UXXoCHhwdOnjyJN998Ey1atECdOnVMGUqJpjndKYo00pJp2Fhbwc/NAdNfrI3jV+7j2n3ee84SsGaJiMj8TDrO0rx585CcnIxWrVrB19dX+/j1118BAHZ2dti6dSvat2+P6tWr46233kLPnj3x559/mjIMi2HuZriCuNjbYs87rbFwUAOUd7SVHQ49AZMlIiLzM3kznD7+/v7YtWuXKVdpkUpaS4pCoUCb6t44Nul5PBe1DYkpGU/+EEnBZImIyPx4I12JSkDFkg6FQoGdb7fGpnEt4OrAWqaSKKekZdpERGUAkyUJzDl0gLEc7KxRzccZRz5sh3c7VpcdDj1GxZolIiKzY7Ikg6ShA4xha22FkS2rYNeEVqjr7yY7HPoPm+GIiMyPyRIVSqFQoLJHOfw2MhwbxjaXHQ4B4NBYRETmx2RJAu3QASW5aikPG2sr1PB1wa4JrdClrp/scMo0NsMREZkfkyUyWGWPcpjRqw7m9KsvO5Qyix28iYjMj8mSBJohFiyjXkmXva01utT1w+EP2uHt9s/IDqfMYZ8lIiLzY7IkkyVmS/+p4KzEy40ryw6jzGGyRERkfkyWJCgtLSnu5eyw4+1W+LJ3XdmhlBlMloiIzM+kI3iTcWTeG85UgjzLIcizHDrX8cUvh65g6p9nZIdUqvFGukRE5seaJQlK4+nO3tYag5sGYftbLWWHUqqxZomIyPyYLEkgLGBQyqIK9CiHRoHussMotXg1HBGR+TFZIpOyslLg1xHP4bfXmsgOpVTiOEtERObHZEkCzb3hSmHFEgD1YJthlcvj308i8HWfugit6IqJEbzPnClkM1kiIjI7dvCmYmNnY4Uez1ZCj2crAQB8XO0xdkWM3KAsHGuWiIjMj8mSBKW5z5I+Xev6IdCjHFIzsrH2+HWsOnpNdkgWh32WiIjMj8mSRKVh6ABjKBQK1PV3AwA0CfZgslQEOSrZERARlT3ss0RSKBQK7Hi7FdrV8JYdikVhMxwRkfkxWZJAe2+4slWxlE+QZzl8068e+jcOkB2KxWAzHBGR+bEZjqRytLPB9BdDEVa5PL7YGIsmIR7wKGeHJsGemLUtDieuJskOsUSpU8lVdghERGUOkyUJNHUDZbxiSUfeq+Y0woM98M/1ZPSeHy0pqpJlTr/66FLXT3YYRERlDpvhqMSyt7VGw8dGA/dxsUfsJx3zzeuktIy8v5q3c75pdta6X8OOtXy0z18IzX1+9f7D4guMiIgKxWRJAm23k7LeaclAs/vWw7h2VbHz7VbY/nZLKG2s0ePZijrznPyovUnXWd0nf1Kjz4gWVbTPFw9uqH1ubaXA32OaAwBcHWyx6c0W+Lh7bfR4tiKWD2uMj7rUROwnHfFNv/qwtlJg5kt1MbZdVe3nv+0fhhAvJwBAq2e8nmaTiIioiCzj5ziVad3qVcw37es+9fBZjzp4e9UJPFfFA1ZWuYnnc1Xc8VJDf5y+noIf9sYDAEa2DMaqI1dxNy0TAFC7oguycwTOJTzQfm7zmy3QfuZuAMCa15ug5uRNAIAGlcujedUKmLn1XwDA/FfCcPJaEpIfZaFuJTc0CCyPKhWcoLSxwtX7j9DymQqY3bceJq87jfmvhKGmnws2jWsBHxd7AMCA5ypjwHOVAQBNQjwBqMegal/TG/a21gCA5a82hpezEgDwx+imuJWSgUDPciYqUSIiMgaTJQkEBGyRDaeMWwCekR2OxbKzscI3/eprXy8a1BBHL9/H+OefgZWVAi/WB45duY9jV5LQ49mKsLe1wqytcWhT3QsLB6lrf67ee4gP157Cq82rINCjHGytFVDaWMPexhqtqlXAztjbGNY8CK2qecHOxgptqnuhmo8zOtb2yRfP+PbVtM+71auIrnX9oPiv9rCaATVVmkQJAJoEe2qfO9rZINCTX1UiIlkUQljetcgpKSlwdXVFcnIyXFxcZIdjtA/X/oM+xwagjlU80HsxULkp4MQmluKQnaNC0qMseDopkZ2jwpHL91G3khsc7KwLnP9hZjasFArY21ojK0eFa/cfIYg1OkREJmGp52/+XJVACKgTJQBYNUj9/2v7Ac9nAGtbaXGVRjbWVvB0UmqfP1fFQ+/8jna5XwlbaysmSkRExA7eMhRYlTevCbB6CJCeDBxcAJxYASRdAVS8vwUREZFMrFkqSc7+oX7kVbsn0PV/gJ2jnJiIiIjKONYsSWBUL7FTvwGf+gIXdgDpKcUWExERERWMNUuWYml39f+T7gLW/LMRERGZC8+6UjzFBYiLXwB8QoH7l9TNcy6+JouKiIiI8mMznKW5ehA4/ANwfiuwoBVwmfdNIyIiKk5MliQw2chWqQnAoo7Ao/smWiARERE9jslSaTAnDIj5RXYUREREpRKTJQlMPmb6w7vA2pHA3plAxoMnz09EREQGY7IkgXiaDt76bJ0CLIoALu4qnuUTERGVQUyWSpuEf4CfugJXDgLZmbKjISIisngcOkACs9y6eGF7wM4JaDAEaDoWKOf55M8QERFRPqxZKs0yU4H93wCLOwMJp2RHQ0REZJGYLElgjoolHbfPAvObAjeOAznZ5l47ERGRRWOyJINZ2uEKsKCVelym5Oty1k9ERGSBTJosRUVFoWHDhnB2doaXlxe6d++O2NhYnXnS09MxatQoeHh4wMnJCT179kRiYqIpwyj5hEreuq8dBmbWBA5+Jy8GIiIiC2LSZGnXrl0YNWoUDhw4gC1btiArKwvt27dHWlqadp4333wTf/75J1atWoVdu3bhxo0b6NGjhynDKPEUyJEdArDhHeDYUtlREBERlXgmvRpu48aNOq8XL14MLy8vHD16FC1atEBycjJ+/PFHLF++HG3atAEALFq0CDVq1MCBAwfw3HPPmTKcEstKlIBkCQD+GA24VwECm8qOhIiIqMQq1j5LycnJAAB3d3cAwNGjR5GVlYV27dpp56levToCAgIQHV34DWEzMjKQkpKi87BkViqJzXCP2/oRcOus7CiIiIhKrGJLllQqFcaNG4emTZuidu3aAICEhATY2dnBzc1NZ15vb28kJCQUuqyoqCi4urpqH/7+/sUVtlmUiGY4jWuHgW/LRo0eERFRURRbsjRq1CicOnUKK1aseOplTZw4EcnJydrH1atXTRChPAqZHbwLMzMUuLBDdhREREQlTrEkS6NHj8b69euxY8cOVKpUSTvdx8cHmZmZSEpK0pk/MTERPj4+hS5PqVTCxcVF52HJrFACk6XkK8DS7rKjICIiKnFMmiwJITB69Gj8/vvv2L59O4KCgnTeDwsLg62tLbZt26adFhsbiytXriA8PNyUoZRoClUJHhhy1SAgK112FERERCWGSa+GGzVqFJYvX45169bB2dlZ2w/J1dUVDg4OcHV1xdChQzF+/Hi4u7vDxcUFb7zxBsLDw8vMlXBACa1Z0jj9O2DvCrT9CHB0lx0NERGRdCatWZo3bx6Sk5PRqlUr+Pr6ah+//vqrdp6ZM2eic+fO6NmzJ1q0aAEfHx+sWbPGlGGUeIqSMnRAYY4uBr6uAeRkyY6EiIhIOpPWLAkDbuNhb2+PuXPnYu7cuaZctUUpkR28H5edDvyzGgjtBVjbyo6GiIhIGt4bTgJFSW6Gy2vtSGBJV+DmSUBVwmvDiIiIigmTJQksomZJ48p+4LvmQPT/ZEdCREQkBZMlCaxK0qCUhtoyGdj9JVCSRh8nIiIyAyZLEigstUlr+8fAhW1AdobsSIiIiMyGyZIEFlmzpLGsF7Cki+woiIiIzIbJkgQW1WepIFcPAr+8DFw9JDsSIiKiYsdkSYISPSiloWL/Aja8IzsKIiKiYsdkSQKrkj4opaFuHAcWdgQubAdunZMdDRERUbEw6aCUZJgSP4K3Ma5EA0tfVD+fkiw3FiIiomLAmiUJSkUzXEGuHeGVckREVOowWZKgVNUs5fVDW2D9eNlREBERmRSb4SSwsvSr4fSJ+Rlw9gaC2wJKZ8C3juyIiIiIngqTJQlKbc2Sxp6v1A8AGBMDuAUAVtZSQyIiIioqNsNJUGr7LBXkm3rAj88DB78DstJlR0NERGQ01ixJUOprlh53/aj6kZ4CtJwgOxoiIiKjsGZJAosfwbuodnwCHJgnOwoiIiKjMFmSoEw1wz1u43vAwgjg3F+yIyEiIjIIkyUJSs0I3kV1ZT+w4mXg7HogJ0t2NERERHoxWZLACmU8WdL4tT+wrBdw5g/ZkRARERWKyZIEZbbPUkEu7gRWDgBSb8mOhIiIqEBMliQo1YNSFtWK/kDsRtlREBER5cNkSQI2wxXg2iHgl5fUQwyomEwSEVHJwXGWJFCU5avhnuT7NkBIO6BGV0DkAP6NgfJBgJ2j7MiIiKiMYrIkgZWKNUt6nd+qfuT10jKgRmc58RARUZnGZjgJyvQ4S0X1a3/gxAr186SrwLaPgQcJcmMiIqIygTVLErAZroh+H6G+x9yNY+rXl/cBQ9gpnIiIihdrliQo84NSPg1NogQAV6KBfd8AQgDxe4DZ9YAL29XvZaXn7yj+6D6Q9chsoRIRUenAZEkKITuA0mPLJGBmbWBJZ+B+PLD0ReBREvBZAPB9K+Dwj0DqbXWi9Hkg8HUN9edOrgR+7qWetzAP76kTMQC4dhS4crBoMQoB5GQX7bNERCQdkyUJFEyWTCvlmu7rzysDORnAzRPAX+OBL0PUiRKgTpoAYM2rwPktwI7pQHoKcGYdcOUAcOh79S1YLuwAvggC/hwLZGcAP7QBFrYHMh4AKTeB4z+ra68K8+h+bs3Wiv7AzFpARqr6Mxd3AdmZJi8GIiIqHuyzRGXPj+1znx9aoH7k9ffbuc+PLVEPZaBx+Adg6xT18+M/A03HAklXAGcf4MZxIPwNIDUBmNcEqNIaGLgWiP3vpsFxm4F/NwEnVwANhgKdvwbO/Q24BwFe/9V4CQEoFKbeYiIiegpMlmRgxZJcV41sTls5IPe5JlEC1H2mrkTrzrt3Zu7zizuAbdNyX6dcVydKAHDkR6BuP2BFP/XrKcnAhnfVNxceuQewdVTXfAU2V78vVICNPcebIiKSgMkSUXHa81Xu880f6r4XPSf3+f1LwMH56uerh6iTo/hduvPbOQHvXy+WMImIqHBMlqRg1RJB3U9KY3bd3OcXdxQ8f2Zq8cZDREQFYgdvGQSTJSoi7jtERGbHZInIkuTwKjoiInNjsiQBhw6gIsvWM1wBEREVCyZLRJaE4zMREZkdkyUZ8vY78W8M9PwRiJgBOLgDXecAb+S5pUf5QLOHRyVYTobsCIiIyhxeDSfRxcB+qDJofu6ERq/mDkj4WjRgowQ8goE7cUDs38CWyXICpZIjm8kSEZG5sWZJCnXNUr6BmvNO8K6pTpQAwLOqeqToCReByk3NEyKVTEyWiIjMjsmSFOK/f428rUU5D6DHAsDvWaB6Z8CvfjHERiUam+GIiMyOzXCWxrUSMDzPoIXHfwb2zgLuxkkLicyIHbyJiMzO5DVLu3fvRpcuXeDn5weFQoG1a9fqvD9o0CAoFAqdR8eOHU0dRslmypED6r8CjDoIdP2fCRdKJRaHDiAiMjuTJ0tpaWmoW7cu5s6dW+g8HTt2xM2bN7WPX375xdRhWAaFiYrfyhp4dgDQIQqwYmVhqcZBKYmIzM7kZ9aIiAhERETonUepVMLHx8fgZWZkZCAjI7evRkpKSpHjKxlUxbPY8NfVV9Sl3QYu7wd+G1o86yF5VNmyIyAiKnOkdPDeuXMnvLy8UK1aNbz22mu4e/eu3vmjoqLg6uqqffj7+5sp0mKW73I4E7C2BVz8gNBewNvngQG/AwFNTL8ekkOVIzsCIqIyx+zJUseOHfHTTz9h27Zt+Pzzz7Fr1y5EREQgJ6fwk8DEiRORnJysfVy9etWMERcDc93txKkCENwGGLIBeO8qMOa4+io6slyCyRIRkbmZvYNL3759tc9DQ0NRp04dBAcHY+fOnWjbtm2Bn1EqlVAqleYKsXSyd1E/+i4DlvUB4jbJjoiKgjVLRERmJ32cpSpVqsDT0xPnz5+XHYrZaG6kqzB2nCVT6fUj0Hc50G2u6TqZk3mIYurvRkREhZJ+6dS1a9dw9+5d+Pr6yg7FjP4blLI4+iwZQukMVO+kfu5dC1jQSk4cZDzWLBERmZ3JqxVSU1MRExODmJgYAEB8fDxiYmJw5coVpKamYsKECThw4AAuXbqEbdu2oVu3bggJCUGHDh1MHUqJJylV0uVXHxi2DajaXnYkZAj2WSIiMjuTJ0tHjhxB/fr1Ub+++lYc48ePR/369TF58mRYW1vj5MmT6Nq1K5555hkMHToUYWFh2LNnT5nqk6QwWw9vA1VqALz0MzDwD9mR0JNw6AAiIrMzeTNcq1atIEThycCmTexYXCLZKIEqLYHBG4GYn9W3UaGSh81wRERmx969MmiSSVl9lvSpHK7u+D1wHeAaIDsaehyb4YiIzI7JklQlMFnSqNIKGL4TaDtZdiSUl4pXwxERmRuTJSpcOQ+g+VvAiD1A2GDZ0RDAmiUiIgmYLMlUEpvhCuJbB+gyS32jXpKLfZaIiMyOyZIUJexqOEOFvw6MiQFqdJEdSdnFq+GIiMyOyZIMJbmD95O4B6mHGei/GvCuLTuasofNcEREZsdkiYqm6vNA78Wyoyh72MGbiMjsmCxJkDsopQXWLOXlWRXo8CngUkl2JGUHa5aIiMyOyZJUFp4sAUD4KGDMMaDnj4CTt+xoSj928CYiMjsmS1Jo+izJjcJkbJRAaC915282zRUv1iwREZkdkyWpSku29B87RyC4rewoSjfWLBERmR2TJQkUeu6dZ/HsXYDRR4BnI2VHUjpx6AAiIrNjsiSRwhKHDjCEZ1Wg01dAn58A37rqabaOcmMqLQSvhiMiMjcb2QFQKWVtC9TsBlTrBCRfVY/PtOkDIPp/siOzbGyGIyIyOyZLUljwoJTGsrZRJ0oA0GqieptTbwNn/wCyHsqNzRKxgzcRkdkxWZKqDCRLeSmdgPafqJ+rvgWmucuNxxKxZomIyOzYZ0mKUtzB21BW1up+TWQc1iwREZkdkyWZyljFUj4NhwFjTwAOrGEyGGuWiIjMjsmSBLlDB5T1bAlA+UBgwgUguE3uNHs3oHZPWRGVbKV52AkiohKKfZakYrIEALCyAvouB26eBCqGqadZ2wAt3wUO/wgc+k5ufCUKkyUiInNjzZIUPOHlY+sABDRWJ0nW/+XwFaoBL3yRf95eC80bm1RMqImIZGOyJFNZGDrAFOq8BHg+ox56YPCGgpvo+q82f1x5uQbkPq/7su57XefkPn/9IBDYXD1I57ORgP9z6n5bNbur36/TF3hpWe78H97SXRab4YiIzI7NcDL8d8KzYrJkmB4L1GWWt7zGngRuxgArBwJBLYCqz+t+JiAcSLoCpFxXv67aHojbrH89z08DtkxWPx9zHPimvvq5nRPg6g/cPqt+XbsnELtBPU5U5WZAWCRQp496/nsXgYjPgOxHwOnfgSZvAPX6A8nX1DF5VQcG/gHkZKhr0zS6fwvU6KKO084JaDRcXbNmYwcM3QIsfwl4dA+slSQiMj8mSxKI/5KlUnu7k+LweFmVr6x+vH8DsPkv6QgfrU5ihu9U36MOAKa4qv+v21d9JdmFberXY2LU/1/aA/zxhjqpqT8gN1nKW1PUcKg6eVnxsvr/+q8A2Znqy/jzJjyvRQOqLEDpDHT7Fnh2oLoWycoaaP1+7nxWVoBVns8BgF05ILRX7usXZuQ+928ENH8L2PyBoaVFREQmxGRJAk1LCmuWTMCuXO7zDtPVj7xG7AGuHQZq9QAqNwV2fwk0GJI7qrh7EFCjK+Dgpn7dfzVgbafuN/X6AeDcX0D4KHVSNGJ37nJt7PLHYmsPwP6/uBx1r/AzFTbDERGZHZMlCQTUN0NlzZIZ+NZRPwDA2Qfo9GX+eTSJEqDbnOdVQ/0oCbT7CpMlIiJzYwdvCVizRMbjvkJEJAuTJQm0fZaseAIkI7EZjojI7JgsSaBJlqyYLJGh2AxHRCQNkyUJBIcOIKNxXyEikoXJkgzaZInFT0ZiMxwRkdnxbC2BStvBW24cZEHYDEdEJA2TJSk0HbxZ/GQoZtZERLLwbC2BStsMJzkQsjxshiMiMjsmS2YmhNCe8BTss0SGYjMcEZE0PFubWY4q92THmiUiIqKSj8mSmWWrhLb3iRX7LJGx2AxHRGR2PFubWY5KQAEOHUBG4phcRETS8GxtZtl5muF4/iPjsWaJiMjcmCyZmU7NEpvhyGD/ZdZshiMiMjuerc0sW6XSPmcHbzIYqyGJiKQxebK0e/dudOnSBX5+flAoFFi7dq3O+0IITJ48Gb6+vnBwcEC7du0QFxdn6jCKLDNb9eSZnkKOSuQ57/EESEREVNLZmHqBaWlpqFu3LoYMGYIePXrke/+LL77AN998gyVLliAoKAiTJk1Chw4dcObMGdjb25s6HIOlZ+Xg07/PYtPpBEzqXBP1/N2gtLFGelYOAOB2agZcHWyRoxJwsLVGOaUNbj/IgKOdNYQA3J3scD8tExnZKjjYWSP5YRbcHG2RkJIOa4UC9rbWSMvM1hk6gMhwbIYjIpLF5MlSREQEIiIiCnxPCIFZs2bhww8/RLdu3QAAP/30E7y9vbF27Vr07dvX1OEY7HrSI6w6cg2PsnIwevnxYl3XV7b/nfDYtEKG4r5CRCSNWfssxcfHIyEhAe3atdNOc3V1RePGjREdHV3o5zIyMpCSkqLzMLXgCk6Y0buOyZdLZFqsWSIiMjeT1yzpk5CQAADw9vbWme7t7a19ryBRUVGYOnVqscYGAJ3r+KFuJTdkZKtwJzUDFd0ccPFOGjyd7JCano0cIeBf3hHnb6ciwN0RqenZyMpRwdNJiUt301DZoxwepGchMSUD3i5K3EvLRIiXE+6nZSEzRwVXBxvE33mIlqd9gdMA+yyR4dgMR0Qki1mTpaKaOHEixo8fr32dkpICf3//YlmXv7sjACDEy0nndUHz5BXoWa7QZVYqn/s8xMsZOPffiY9NK2Qo7itERNKYtRnOx8cHAJCYmKgzPTExUfteQZRKJVxcXHQeRGUTa5aIiMzNrMlSUFAQfHx8sG3bNu20lJQUHDx4EOHh4eYMRS5tUwprC8hQbIYjIpLF5M1wqampOH/+vPZ1fHw8YmJi4O7ujoCAAIwbNw6ffPIJqlatqh06wM/PD927dzd1KESlB5vhiIikMXmydOTIEbRu3Vr7WtPXKDIyEosXL8Y777yDtLQ0DB8+HElJSWjWrBk2btwodYwl8+PQAVRUrFkiIjI3kydLrVq1gtDTVKBQKDBt2jRMmzbN1KsmKsXYDEdEJAvvDScD+yyRsVgLSUQkDZMlIovCmiUiInNjsiQF+yyRsbivEBHJwmRJBjbDkbEU7LNERCQLkyUii8JkiYjI3JgsScFmODIW9xUiIlmYLBFZEjbDERGZHZMlGdhniYylrYVkskREZG5MlogsAhNrIiJZmCxJwT5LVERshiMiMjsmSzLwhEfGYjMcEZE0TJaIiIiI9GCyJAVrB6iIWCtJRGR2TJZkYp8lMhT3FSIiaZgsycChA8ho3FeIiGRhskRkSdgMR0RkdkyWZGLTChmK+woRkTRMlogsAocOICKShcmSDOyzREXFZjgiIrNjsiQFT3hkJDbDERFJw2RJJp4AyWBshiMikoXJkgxsSqGi4r5DRGR2TJakYs0SGYi1kERE0jBZkuK/2gGeAMlgbIYjIpKFyRKRJWEzHBGR2TFZkoFDB5CxWAtJRCQNkyUii8KaJSIic2OyJAX7LJGx/ttX2AxHRGR2TJZk4AmPjMXEmohIGiZLUvEESMZiok1EZG5MlqTgCY+MxWY4IiJZmCzJxKYVMhT3FSIiaZgsycChA6jIWLNERGRuTJaILAITayIiWZgsScGhA8hICvZZIiKShcmSDDzhUZFx3yEiMjcmS1KxZokMxX2FiEgWJktSsHaAjKTJlVgrSURkdkyWZGKfJTIakyUiInNjsiQDawfIaEysiYhkYbIkFU+AZCDt1XBywyAiKouYLEnBoQOoqJgtERGZm9mTpSlTpkChUOg8qlevbu4wiCwME2siIllsZKy0Vq1a2Lp1a24QNlLCkId9lqiouO8QEZmdlCzFxsYGPj4+MlZNZJm0TbZMloiIzE1Kn6W4uDj4+fmhSpUq6N+/P65cuaJ3/oyMDKSkpOg8SgX2WSKDcV8hIpLF7MlS48aNsXjxYmzcuBHz5s1DfHw8mjdvjgcPHhT6maioKLi6umof/v7+Zoy4GLAphYqK+w4RkdmZPVmKiIhA7969UadOHXTo0AF///03kpKSsHLlykI/M3HiRCQnJ2sfV69eNWPExYm1BWQgNsMREUkjvWe1m5sbnnnmGZw/f77QeZRKJZRKpRmjKm484ZGxmFgTEckifZyl1NRUXLhwAb6+vrJDMT/2WSJjsRmOiMjszJ4svf3229i1axcuXbqE/fv348UXX4S1tTX69etn7lDk0Z7wmCyRgZhYExFJY/ZmuGvXrqFfv364e/cuKlSogGbNmuHAgQOoUKGCuUMhsiDss0REJIvZk6UVK1aYe5UlEG93QkXEZjgiIrOT3mepTOIJj4zFxJqISBomS1LxBEiGYjMcEZEsTJak4AmPioi1kkREZsdkSSY2rZChuK8QEUnDZEkG1g6Q0dgMR0QkC5MlqVhbQEZiok1EZHZMlqTg0AFkJO4rRETSMFmSgbUDZDQ2wxERycJkSSrWFpCRmGgTEZkdkyUpeMIjI7EZjohIGiZLMvEESEZjok1EZG5MlmRgUwoZ7b/EmrsOEZHZMVmSijVLZCDuKkRE0jBZkoLVA1RU3HeIiMyNyZJM7LNEBuO+QkQkC5MlGdhniYylSay57xARmR2TJSk0JzzWFpCxmCwREZkbkyWZ2AxHBuO+QkQkC5MlGdiUQsZiMxwRkTRMlqRibQEZi8kSEZG5MVmSgic8MhYTayIiWZgsycTzHxmKzXBERNIwWZJB8Go4KiomS0RE5sZkSQZVtvp/Kxu5cZAFYWJNRCQLkyUZVDnq/5kskaHYDEdEJA2TJRlYs0RFxmSJiMjcmCzJwJolMhqb4YiIZGGyJIO2ZslabhxkOdgMR0QkDZMlGdgMR0XGZImIyNyYLMnAZImMxmY4IiJZmCzJoO2zxGY4MhKb4YiIzI7Jkgzss0TGUrBmiYhIFiZLMgheDUfG0iRLrFkiIjI3JksysM8SFRWb4YiIzI7JkrkJwWSJjMdmOCIiaZgsmZtQ5T5nskQGYzMcEZEsTJbMTVOrBLCDNxmPuRIRkdkxWTI3nWSJNUtkIDbDERFJw2TJ3PImSwrWLJGxWLVERGRuTJbMTTMgJcCaJTIer4YjIjI7JkvmppMssWaJDMRmOCIiaZgsmZumGU5hzRMgGYFXwxERySItWZo7dy4CAwNhb2+Pxo0b49ChQ7JCyRW/Gzj4XfGug2Ms0dNgMxwRkdlJOWP/+uuvGD9+PObPn4/GjRtj1qxZ6NChA2JjY+Hl5SUjJCDhFLCkKwABbHin8PkU1rm3K9Fw9AQe3jFufTkZRodIZRhrIYmIpFEIYf6fqo0bN0bDhg3xv//9DwCgUqng7++PN954A++9916++TMyMpCRkZtcpKSkwN/fH8nJyXBxcTFdYL+/BpxYbrrl6ePqD7x5yjzrIst3/zIwu466RrLhq7KjIaKyIOIzky8yJSUFrq6upj9/FzOz1yxlZmbi6NGjmDhxonaalZUV2rVrh+jo6AI/ExUVhalTpxZ/cN3mAh5VgIu71M1lrv7q/3MygcRTgG/d3A7a9q7AjeOAdy31qNw29oCNErh7Qf360X3AvYr6fytr9fy3Y4HyQern4a8X//ZQ6WHnBECh3h8PzpMdDRGVBcWQLFkqsydLd+7cQU5ODry9vXWme3t749y5cwV+ZuLEiRg/frz2taZmyeSsrIAWE9QPopKknAfQZwlw84TsSIiIyhyL6GWsVCqhVCplh0EkV81u6gcREZmV2a+G8/T0hLW1NRITE3WmJyYmwsfHx9zhEBEREell9mTJzs4OYWFh2LZtm3aaSqXCtm3bEB4ebu5wiIiIiPSS0gw3fvx4REZGokGDBmjUqBFmzZqFtLQ0DB48WEY4RERERIWSkiy99NJLuH37NiZPnoyEhATUq1cPGzduzNfpm4iIiEg2KeMsPS1LHaeBiIioLLPU8zfvDUdERESkB5MlIiIiIj2YLBERERHpwWSJiIiISA8mS0RERER6MFkiIiIi0oPJEhEREZEeTJaIiIiI9JAygvfT0oyjmZKSIjkSIiIiMpTmvG1p42FbZLL04MEDAIC/v7/kSIiIiMhYDx48gKurq+wwDGaRtztRqVS4ceMGnJ2doVAoTLbclJQU+Pv74+rVqxY1DHtxYFnkYlnkYlnoYnnkYlnkYlnkerwshBB48OAB/Pz8YGVlOT2BLLJmycrKCpUqVSq25bu4uJT5HVyDZZGLZZGLZaGL5ZGLZZGLZZErb1lYUo2ShuWkdUREREQSMFkiIiIi0oPJUh5KpRIfffQRlEql7FCkY1nkYlnkYlnoYnnkYlnkYlnkKi1lYZEdvImIiIjMhTVLRERERHowWSIiIiLSg8kSERERkR5MloiIiIj0YLJEREREpAeTpTzmzp2LwMBA2Nvbo3Hjxjh06JDskEwqKioKDRs2hLOzM7y8vNC9e3fExsbqzJOeno5Ro0bBw8MDTk5O6NmzJxITE3XmuXLlCjp16gRHR0d4eXlhwoQJyM7ONuemmNxnn30GhUKBcePGaaeVpbK4fv06XnnlFXh4eMDBwQGhoaE4cuSI9n0hBCZPngxfX184ODigXbt2iIuL01nGvXv30L9/f7i4uMDNzQ1Dhw5FamqquTflqeTk5GDSpEkICgqCg4MDgoOD8fHHH+vc9LM0l8Xu3bvRpUsX+Pn5QaFQYO3atTrvm2rbT548iebNm8Pe3h7+/v744osvinvTjKavLLKysvDuu+8iNDQU5cqVg5+fHwYOHIgbN27oLKMslMXjRo4cCYVCgVmzZulMt/iyECSEEGLFihXCzs5OLFy4UJw+fVq8+uqrws3NTSQmJsoOzWQ6dOggFi1aJE6dOiViYmLECy+8IAICAkRqaqp2npEjRwp/f3+xbds2ceTIEfHcc8+JJk2aaN/Pzs4WtWvXFu3atRPHjx8Xf//9t/D09BQTJ06UsUkmcejQIREYGCjq1Kkjxo4dq51eVsri3r17onLlymLQoEHi4MGD4uLFi2LTpk3i/Pnz2nk+++wz4erqKtauXStOnDghunbtKoKCgsSjR4+083Ts2FHUrVtXHDhwQOzZs0eEhISIfv36ydikIps+fbrw8PAQ69evF/Hx8WLVqlXCyclJzJ49WztPaS6Lv//+W3zwwQdizZo1AoD4/fffdd43xbYnJycLb29v0b9/f3Hq1Cnxyy+/CAcHB/Hdd9+ZazMNoq8skpKSRLt27cSvv/4qzp07J6Kjo0WjRo1EWFiYzjLKQlnktWbNGlG3bl3h5+cnZs6cqfOepZcFk6X/NGrUSIwaNUr7OicnR/j5+YmoqCiJURWvW7duCQBi165dQgj1AcDW1lasWrVKO8/Zs2cFABEdHS2EUH9prKysREJCgnaeefPmCRcXF5GRkWHeDTCBBw8eiKpVq4otW7aIli1bapOlslQW7777rmjWrFmh76tUKuHj4yNmzJihnZaUlCSUSqX45ZdfhBBCnDlzRgAQhw8f1s6zYcMGoVAoxPXr14sveBPr1KmTGDJkiM60Hj16iP79+wshylZZPH5SNNW2f/vtt6J8+fI635F3331XVKtWrZi3qOj0JQgahw4dEgDE5cuXhRBlryyuXbsmKlasKE6dOiUqV66skyyVhrJgMxyAzMxMHD16FO3atdNOs7KyQrt27RAdHS0xsuKVnJwMAHB3dwcAHD16FFlZWTrlUL16dQQEBGjLITo6GqGhofD29tbO06FDB6SkpOD06dNmjN40Ro0ahU6dOulsM1C2yuKPP/5AgwYN0Lt3b3h5eaF+/fr4/vvvte/Hx8cjISFBpyxcXV3RuHFjnbJwc3NDgwYNtPO0a9cOVlZWOHjwoPk25ik1adIE27Ztw7///gsAOHHiBPbu3YuIiAgAZassHmeqbY+OjkaLFi1gZ2ennadDhw6IjY3F/fv3zbQ1ppecnAyFQgE3NzcAZassVCoVBgwYgAkTJqBWrVr53i8NZcFkCcCdO3eQk5Ojc9IDAG9vbyQkJEiKqnipVCqMGzcOTZs2Re3atQEACQkJsLOz037ZNfKWQ0JCQoHlpHnPkqxYsQLHjh1DVFRUvvfKUllcvHgR8+bNQ9WqVbFp0ya89tprGDNmDJYsWQIgd1v0fT8SEhLg5eWl876NjQ3c3d0tqizee+899O3bF9WrV4etrS3q16+PcePGoX///gDKVlk8zlTbXlq+N3mlp6fj3XffRb9+/eDi4gKgbJXF559/DhsbG4wZM6bA90tDWdjIDoDkGDVqFE6dOoW9e/fKDkWKq1evYuzYsdiyZQvs7e1lhyOVSqVCgwYN8OmnnwIA6tevj1OnTmH+/PmIjIyUHJ15rVy5EsuWLcPy5ctRq1YtxMTEYNy4cfDz8ytzZUGGycrKQp8+fSCEwLx582SHY3ZHjx7F7NmzcezYMSgUCtnhFBvWLAHw9PSEtbV1viudEhMT4ePjIymq4jN69GisX78eO3bsQKVKlbTTfXx8kJmZiaSkJJ3585aDj49PgeWkec9SHD16FLdu3cKzzz4LGxsb2NjYYNeuXfjmm29gY2MDb2/vMlMWvr6+qFmzps60GjVq4MqVKwByt0Xf98PHxwe3bt3SeT87Oxv37t2zqLKYMGGCtnYpNDQUAwYMwJtvvqmtfSxLZfE4U217afneALmJ0uXLl7FlyxZtrRJQdspiz549uHXrFgICArTH0suXL+Ott95CYGAggNJRFkyWANjZ2SEsLAzbtm3TTlOpVNi2bRvCw8MlRmZaQgiMHj0av//+O7Zv346goCCd98PCwmBra6tTDrGxsbhy5Yq2HMLDw/HPP//o7Piag8TjJ9ySrG3btvjnn38QExOjfTRo0AD9+/fXPi8rZdG0adN8Q0j8+++/qFy5MgAgKCgIPj4+OmWRkpKCgwcP6pRFUlISjh49qp1n+/btUKlUaNy4sRm2wjQePnwIKyvdw6K1tTVUKhWAslUWjzPVtoeHh2P37t3IysrSzrNlyxZUq1YN5cuXN9PWPD1NohQXF4etW7fCw8ND5/2yUhYDBgzAyZMndY6lfn5+mDBhAjZt2gSglJSF7B7mJcWKFSuEUqkUixcvFmfOnBHDhw8Xbm5uOlc6WbrXXntNuLq6ip07d4qbN29qHw8fPtTOM3LkSBEQECC2b98ujhw5IsLDw0V4eLj2fc3l8u3btxcxMTFi48aNokKFChZ3uXxB8l4NJ0TZKYtDhw4JGxsbMX36dBEXFyeWLVsmHB0dxc8//6yd57PPPhNubm5i3bp14uTJk6Jbt24FXjJev359cfDgQbF3715RtWpVi7hcPq/IyEhRsWJF7dABa9asEZ6enuKdd97RzlOay+LBgwfi+PHj4vjx4wKA+Prrr8Xx48e1V3iZYtuTkpKEt7e3GDBggDh16pRYsWKFcHR0LDGXiGvoK4vMzEzRtWtXUalSJRETE6NzPM17NVdZKIuCPH41nBCWXxZMlvKYM2eOCAgIEHZ2dqJRo0biwIEDskMyKQAFPhYtWqSd59GjR+L1118X5cuXF46OjuLFF18UN2/e1FnOpUuXREREhHBwcBCenp7irbfeEllZWWbeGtN7PFkqS2Xx559/itq1awulUimqV68uFixYoPO+SqUSkyZNEt7e3kKpVIq2bduK2NhYnXnu3r0r+vXrJ5ycnISLi4sYPHiwePDggTk346mlpKSIsWPHioCAAGFvby+qVKkiPvjgA50TYGkuix07dhR4jIiMjBRCmG7bT5w4IZo1ayaUSqWoWLGi+Oyzz8y1iQbTVxbx8fGFHk937NihXUZZKIuCFJQsWXpZKITIMzQtEREREelgnyUiIiIiPZgsEREREenBZImIiIhIDyZLRERERHowWSIiIiLSg8kSERERkR5MloiIiIj0YLJEREREpAeTJSIiIiI9mCwRERER6cFkiYiIiEiP/wPQJ7cKr/ChiwAAAABJRU5ErkJggg==" }, "metadata": {}, "output_type": "display_data" } ], "execution_count": 19 }, { "cell_type": "markdown", "source": [ "You can put these transformers in a pipeline to apply to both train/test split\n" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "source": [ "from sklearn.metrics import accuracy_score\n", "\n", "# Unequal length univariate data\n", "from aeon.pipeline import make_pipeline\n", "\n", "train_X, train_y = load_plaid(split=\"Train\")\n", "test_X, test_y = load_plaid(split=\"Test\")\n", "steps = [truncate, rc]\n", "pipe = make_pipeline(steps)\n", "pipe.fit(train_X, train_y)\n", "preds = pipe.predict(test_X)\n", "accuracy_score(train_y, preds)" ], "metadata": { "collapsed": false, "pycharm": { "is_executing": true }, "ExecuteTime": { "end_time": "2024-11-17T13:50:05.966304Z", "start_time": "2024-11-17T13:49:37.145088Z" } }, "outputs": [ { "data": { "text/plain": [ "0.813780260707635" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 20 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Missing Values\n", "\n", "Missing values are indicated by `NaN` in numpy array. You can test whether any `aeon`\n", " data structure contains missing values using the utility function" ] }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:50:06.076875Z", "start_time": "2024-11-17T13:50:06.065907Z" } }, "cell_type": "code", "source": [ "X = np.random.random(size=(10, 2, 200))\n", "has_missing(X)" ], "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 21 }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:50:06.186109Z", "start_time": "2024-11-17T13:50:06.180126Z" } }, "cell_type": "code", "source": [ "X[5][0][55] = np.NAN\n", "has_missing(X)" ], "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 22 }, { "metadata": {}, "cell_type": "markdown", "source": [ "There are a range of strategies for handling missing values. These include:\n", "\n", "1. Use an estimator that internally handles missing values. It is fairly easy for\n", "some algorithms (such as decision trees) to internally deal with missing values,\n", "usually be using it as a distinct series value after discretisation. We do not yet \n", "have many estimators with this capability. Estimators that are able to internally \n", "handle missing values are tagged with `\"capability:missing_values\": True`." ] }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:50:06.436013Z", "start_time": "2024-11-17T13:50:06.296331Z" } }, "cell_type": "code", "source": [ "from aeon.utils.discovery import all_estimators\n", "\n", "all_estimators(\n", " tag_filter={\"capability:missing_values\": True},\n", ")" ], "outputs": [ { "data": { "text/plain": [ "[('BORF', aeon.transformations.collection.dictionary_based._borf.BORF),\n", " ('CollectionId',\n", " aeon.transformations.collection.compose._identity.CollectionId),\n", " ('DummyClassifier', aeon.classification.dummy.DummyClassifier),\n", " ('DummyRegressor', aeon.regression._dummy.DummyRegressor),\n", " ('RandomSegmenter', aeon.segmentation._random.RandomSegmenter),\n", " ('STRAY', aeon.anomaly_detection._stray.STRAY),\n", " ('SimpleImputer', aeon.transformations.collection._impute.SimpleImputer)]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 23 }, { "metadata": {}, "cell_type": "markdown", "source": [ "2. Removing series with missing: this is often desirable if the train set size is\n", "large, the number of series with missing is small and the proportion of missing\n", "values for these series is high.\n", "\n", "We do not yet have a transformer for this, but it is easy to implement yourself.\n", "\n", "3. Interpolating missing values from series: estimating the missing values from the \n", "other values in a time series is commonly done. This is\n", " often desirable if the train set size is small and the proportion of missing values\n", " is low. You can do this with the transformer ``SimpleImputer``. This interpolates \n", " each series and each channel independently. So for example a mean interpolation \n", " of series with two channels `[[NaN,1.0,2.0,3.0],[-1.0,-2.0,-3.0,-4.0]]` would be \n", " `[[2.0,1.0,2.0,3.0],[-1.0,-2.0,-3.0,-4.0]]`. " ] }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:52:23.133162Z", "start_time": "2024-11-17T13:52:23.118202Z" } }, "cell_type": "code", "source": [ "from aeon.transformations.collection import SimpleImputer\n", "\n", "imput = SimpleImputer(strategy=\"mean\")\n", "X2 = imput.fit_transform(X)\n", "has_missing(X2)" ], "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 26 }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:52:23.825897Z", "start_time": "2024-11-17T13:52:23.811936Z" } }, "cell_type": "code", "source": [ "imp2 = SimpleImputer(strategy=\"median\")\n", "X3 = imp2.fit_transform(X)\n", "has_missing(X3)" ], "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 27 }, { "metadata": { "ExecuteTime": { "end_time": "2024-11-17T13:52:24.602058Z", "start_time": "2024-11-17T13:52:24.582111Z" } }, "cell_type": "code", "source": [ "imp3 = SimpleImputer(strategy=\"constant\", fill_value=0)\n", "X4 = imp3.fit_transform(X)\n", "has_missing(X4)" ], "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "execution_count": 28 }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": "" } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.8" } }, "nbformat": 4, "nbformat_minor": 4 }