From b88ee691a020ff2d1e88e809a4de751b0d8533d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A2=D1=8E=D1=80?= =?UTF-8?q?=D0=BD=D0=B8=D0=BD?= Date: Wed, 14 May 2025 18:36:00 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B7=D0=B0=D0=B2=D0=B5=D1=80=D1=88=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ..._Malawi_National_Football_Team_Matches.csv | 74 +++ moydatabase.ipynb | 209 ++++++++ week4_scikit_learn.ipynb | 487 ++++++++++++++++++ 3 files changed, 770 insertions(+) create mode 100644 Dataset_Malawi_National_Football_Team_Matches.csv create mode 100644 moydatabase.ipynb create mode 100644 week4_scikit_learn.ipynb diff --git a/Dataset_Malawi_National_Football_Team_Matches.csv b/Dataset_Malawi_National_Football_Team_Matches.csv new file mode 100644 index 0000000..c3869bc --- /dev/null +++ b/Dataset_Malawi_National_Football_Team_Matches.csv @@ -0,0 +1,74 @@ +Date,Opponent,Team Score,Opponent Score,Result,Venue,Competition +"~1957","Northern Rhodesia",0,5,Loss,Unknown,"Friendly (First International)" +"~1962",Ghana,0,12,Loss,Unknown,Friendly +"~1968",Botswana,8,1,Win,Unknown,Friendly +02/06/1996,"South Africa",0,3,Loss,Away,"World Cup Qualifier" +07/07/1996,Zambia,1,1,Draw,Home,Friendly +08/01/2010,Algeria,0,3,Loss,Away,"AFCON 2010 Group Stage" +11/01/2010,Angola,0,2,Loss,Away,"AFCON 2010 Group Stage" +14/01/2010,Mali,1,3,Loss,Away,"AFCON 2010 Group Stage" +04/09/2010,Tunisia,2,2,Draw,Away,"AFCON Qualifier" +09/10/2010,Chad,6,2,Win,Home,"AFCON Qualifier" +17/11/2010,Rwanda,2,1,Win,Home,Friendly +11/11/2011,Kenya,0,0,Draw,Away,Friendly +02/06/2012,Kenya,0,0,Draw,Away,"World Cup Qualifier" +09/06/2012,Nigeria,0,2,Loss,Home,"World Cup Qualifier" +08/09/2012,Ghana,0,1,Loss,Away,"AFCON Qualifier" +13/10/2012,Ghana,0,1,Loss,Home,"AFCON Qualifier" +05/06/2013,Namibia,0,0,Draw,Away,"World Cup Qualifier" +12/06/2013,Kenya,2,2,Draw,Home,"World Cup Qualifier" +07/09/2013,Nigeria,0,2,Loss,Away,"World Cup Qualifier" +15/06/2015,Egypt,1,2,Loss,Away,"AFCON Qualifier" +06/09/2015,Eswatini,2,2,Draw,Home,"AFCON Qualifier" +07/10/2016,Ghana,0,0,Draw,Away,"World Cup Qualifier" +10/06/2017,Comoros,1,0,Win,Home,"AFCON Qualifier" +08/09/2018,Morocco,0,3,Loss,Away,"AFCON Qualifier" +16/10/2018,Cameroon,0,0,Draw,Home,"AFCON Qualifier" +12/11/2020,"Burkina Faso",1,3,Loss,Away,"AFCON Qualifier" +16/11/2020,"Burkina Faso",0,0,Draw,Home,"AFCON Qualifier" +17/03/2021,Ethiopia,0,4,Loss,Away,Friendly +24/03/2021,"South Sudan",1,0,Win,Away,"AFCON Qualifier" +29/03/2021,Uganda,1,0,Win,Home,"AFCON Qualifier (Qualified)" +13/06/2021,Tanzania,0,2,Loss,Away,Friendly +09/07/2021,Zimbabwe,2,2,Draw,Home,"COSAFA Cup" +11/07/2021,Mozambique,0,2,Loss,Away,"COSAFA Cup" +13/07/2021,Namibia,1,1,Draw,Home,"COSAFA Cup" +14/07/2021,Senegal,1,2,Loss,Away,"COSAFA Cup" +03/09/2021,Cameroon,0,2,Loss,Away,"World Cup Qualifier" +07/09/2021,Mozambique,1,0,Win,Home,"World Cup Qualifier" +08/10/2021,"Cote d'Ivoire",0,3,Loss,Home,"World Cup Qualifier" +11/10/2021,"Cote d'Ivoire",1,2,Loss,Away,"World Cup Qualifier" +13/11/2021,Cameroon,0,4,Loss,Home,"World Cup Qualifier" +16/11/2021,Mozambique,0,1,Loss,Away,"World Cup Qualifier" +31/12/2021,Comoros,1,0,Win,Away,Friendly +10/01/2022,Guinea,0,1,Loss,Away,"AFCON 2021 Group Stage" +14/01/2022,Zimbabwe,2,1,Win,Home,"AFCON 2021 Group Stage" +18/01/2022,Senegal,0,0,Draw,Home,"AFCON 2021 Group Stage" +25/01/2022,Morocco,1,2,Loss,Away,"AFCON 2021 Round of 16" +05/06/2022,Ethiopia,2,1,Win,Home,"AFCON Qualifier" +09/06/2022,Guinea,0,1,Loss,Away,"AFCON Qualifier" +06/07/2022,Lesotho,1,2,Loss,Away,"COSAFA Cup" +08/07/2022,Eswatini,1,1,Draw,Home,"COSAFA Cup" +10/07/2022,Mauritius,2,0,Win,Away,"COSAFA Cup" +27/08/2022,Mozambique,1,1,Draw,Home,"CHAN Qualifier" +04/09/2022,Mozambique,0,0,Draw,Away,"CHAN Qualifier" +25/02/2023,Lesotho,1,1,Draw,Home,Friendly +15/03/2023,Bangladesh,1,1,Draw,Away,Friendly +24/03/2023,Egypt,0,2,Loss,Away,"AFCON Qualifier" +28/03/2023,Egypt,0,4,Loss,Home,"AFCON Qualifier" +14/06/2023,Mozambique,1,1,Draw,Away,"Four Nations Tournament" +20/06/2023,Ethiopia,0,0,Draw,Home,"AFCON Qualifier" +09/09/2023,Guinea,2,2,Draw,Home,"AFCON Qualifier" +17/11/2023,Liberia,0,1,Loss,Away,"World Cup Qualifier" +21/11/2023,Tunisia,1,0,Win,Home,"World Cup Qualifier" +23/03/2024,Senegal,0,1,Loss,Away,Friendly +26/03/2024,Kenya,0,4,Loss,Home,"Four Nations Tournament" +06/06/2024,"Sao Tome",3,1,Win,Home,"World Cup Qualifier" +10/06/2024,"Eq. Guinea",0,1,Loss,Away,"World Cup Qualifier" +10/09/2024,"Burkina Faso",3,0,Win,Home,"AFCON 2025 Qualifier" +11/10/2024,Senegal,0,4,Loss,Away,"AFCON 2025 Qualifier" +14/11/2024,Burundi,0,0,Draw,Home,"AFCON 2025 Qualifier" +18/11/2024,"Burkina Faso",0,1,Loss,Away,"AFCON 2025 Qualifier" +02/03/2025,Comoros,2,0,Win,Away,Friendly +20/03/2025,Namibia,0,1,Loss,Away,"World Cup Qualifier" +24/03/2025,Tunisia,,,TBD,Away,"World Cup Qualifier" \ No newline at end of file diff --git a/moydatabase.ipynb b/moydatabase.ipynb new file mode 100644 index 0000000..764bb6e --- /dev/null +++ b/moydatabase.ipynb @@ -0,0 +1,209 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "793785ea-f1d5-433a-9f01-44b378a5c3df", + "metadata": {}, + "source": [ + "1. Загрузка и объединение текстовых признаков" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "75eb3861-598c-4313-91cb-f9d4f09e0dc4", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "# Загрузка CSV\n", + "df = pd.read_csv(\"Dataset_Malawi_National_Football_Team_Matches.csv\")\n", + "\n", + "# Объединяем категориальные текстовые колонки в один текстовый столбец\n", + "df[\"text\"] = df[[\"Opponent\", \"Result\", \"Venue\", \"Competition\"]].fillna(\"\").agg(\" \".join, axis=1)\n", + "texts = df[\"text\"].tolist()\n" + ] + }, + { + "cell_type": "markdown", + "id": "77b3eb9f-f116-476f-b24c-9d0c61b9d660", + "metadata": {}, + "source": [ + "2. Подготовка функций токенизации" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "b815fe8c-0362-4bb4-8d7f-5763de7192b0", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "from collections import defaultdict\n", + "\n", + "def tokenize(doc):\n", + " return (tok.lower() for tok in re.findall(r\"\\w+\", doc))\n", + "\n", + "def token_freqs(doc):\n", + " freq = defaultdict(int)\n", + " for tok in tokenize(doc):\n", + " freq[tok] += 1\n", + " return freq\n" + ] + }, + { + "cell_type": "markdown", + "id": "69867160-c219-4bff-b268-679a8abe2498", + "metadata": {}, + "source": [ + "3. Сравнение методов векторизации" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "527d49e0-5e45-4e6d-bdd5-d743b69e56f1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DictVectorizer: (73, 70) — 0.00s\n", + "FeatureHasher: (73, 4096) — 0.00s\n", + "CountVectorizer: (73, 69) — 0.00s\n", + "HashingVectorizer: (73, 4096) — 0.00s\n", + "TfidfVectorizer: (73, 69) — 0.00s\n" + ] + } + ], + "source": [ + "from sklearn.feature_extraction import DictVectorizer, FeatureHasher\n", + "from sklearn.feature_extraction.text import CountVectorizer, HashingVectorizer, TfidfVectorizer\n", + "import numpy as np\n", + "from time import time\n", + "\n", + "def n_nonzero_columns(X):\n", + " return len(np.unique(X.nonzero()[1]))\n", + "\n", + "data_size_mb = sum(len(s.encode(\"utf-8\")) for s in texts) / 1e6\n", + "vectorizer_stats = defaultdict(list)\n", + "\n", + "# DictVectorizer\n", + "t0 = time()\n", + "dv = DictVectorizer()\n", + "X_dv = dv.fit_transform(token_freqs(d) for d in texts)\n", + "duration = time() - t0\n", + "vectorizer_stats[\"vectorizer\"].append(\"DictVectorizer\")\n", + "vectorizer_stats[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"DictVectorizer: {X_dv.shape} — {duration:.2f}s\")\n", + "\n", + "# FeatureHasher\n", + "t0 = time()\n", + "fh = FeatureHasher(n_features=2**12)\n", + "X_fh = fh.transform(token_freqs(d) for d in texts)\n", + "duration = time() - t0\n", + "vectorizer_stats[\"vectorizer\"].append(\"FeatureHasher\")\n", + "vectorizer_stats[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"FeatureHasher: {X_fh.shape} — {duration:.2f}s\")\n", + "\n", + "# CountVectorizer\n", + "t0 = time()\n", + "cv = CountVectorizer()\n", + "X_cv = cv.fit_transform(texts)\n", + "duration = time() - t0\n", + "vectorizer_stats[\"vectorizer\"].append(\"CountVectorizer\")\n", + "vectorizer_stats[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"CountVectorizer: {X_cv.shape} — {duration:.2f}s\")\n", + "\n", + "# HashingVectorizer\n", + "t0 = time()\n", + "hv = HashingVectorizer(n_features=2**12)\n", + "X_hv = hv.fit_transform(texts)\n", + "duration = time() - t0\n", + "vectorizer_stats[\"vectorizer\"].append(\"HashingVectorizer\")\n", + "vectorizer_stats[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"HashingVectorizer: {X_hv.shape} — {duration:.2f}s\")\n", + "\n", + "# TfidfVectorizer\n", + "t0 = time()\n", + "tv = TfidfVectorizer()\n", + "X_tv = tv.fit_transform(texts)\n", + "duration = time() - t0\n", + "vectorizer_stats[\"vectorizer\"].append(\"TfidfVectorizer\")\n", + "vectorizer_stats[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"TfidfVectorizer: {X_tv.shape} — {duration:.2f}s\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "503cdc6e-e68e-4618-97e1-1535cfc36788", + "metadata": {}, + "source": [ + "4. Визуализация сравнения" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "bdfd45e3-03fa-47f5-b9e4-4d286e50e6dd", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6IAAAIjCAYAAAAZY6ZOAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUzFJREFUeJzt3QW4VVX6P/AFEooCiiiIooLd3YWJ3d3tGGPnzCgydreOMXa3jmN3d2IH2B2AMsbA+T/v+v33nXOTe4lNfT7Pc7zcc/bZsc4+x/O971prt6pUKpUEAAAAJWld1oYAAAAgCKIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFGIs+/PDDtMcee6TevXunySefPHXq1Cktt9xy6ayzzkr/+c9/xvXuAQCME23GzWYBJn7//ve/02abbZbat2+ftt9++zT//POn33//PT355JPpkEMOSW+++Wa66KKLxvVuAgCUrlWlUqmUv1mAidvAgQPTggsumGaaaab08MMPpxlmmKHW4x988EEOqvvtt98420cAgHFF11yAseDkk09OP//8c/rnP/9ZL4SG2WefvVYIbdWqVdpnn33SNddck+aaa67cjXexxRZLjz/+eK3nffzxx2mvvfbKy0wxxRRp2mmnzVXXQYMG1Vru8ssvz+ssbh06dEgLLLBAuuSSS2ott+OOO6apppqq3v7dfPPN+XmPPvporfufe+65tOaaa6bOnTvnda600krpqaeeqrXM0UcfnZ/73Xff1br/xRdfzPfHvlVvf9ZZZ6213KeffpqPLZate1z33HNPWmGFFdKUU06ZOnbsmNZZZ51cWW6On376KR1wwAF5e1Gljj8SRKW62M841rrH/MUXX+TlF1988fx6Fr755pu0yy67pG7duuXXaqGFFkpXXHFFre3Fvsf6Tj311HTGGWekWWaZJR9XtNmAAQPq7V/8waI4tqmnnjptsMEG6e2336633Oeff5633aNHj3wcvXr1SnvuuWeuttd93Ru6Fe3f0ravK55fvd5pppkm9enTJz3xxBP1lm3O69bQ/lx99dWpdevW6cQTT2xwmw3dqvf7/PPPT/PNN19up2ivvffeO58H1WKfo7fCSy+9lJZddtl8/NGm//jHP2ot19D5EeJY4v447wsXXHBBPififRLHHP+Oz4Jqr7/+ej6eott+9+7d084775y+//77UX4/Fedc9X0hjjvuj+0VinOlur1GjBiR/4DW0DqqteQ8C++8807adNNNU5cuXfKxxvvpzjvvbHCd1fsT50icV+uuu27673//26z3cvE6NXWrfq1eeeWVtNZaa+VhE/FZuOqqq6Znn312lD5PgZbRNRdgLPjXv/6Vv2DGF9vmeuyxx9INN9yQ9t133/zlKr5ER+h7/vnn8xfl8MILL6Snn346bbnllvnLV3xpiy+98WX6rbfeyl+QqkUA6tq1axoyZEi69NJL02677Za/vK222motPqYISvGFLQJyv379ckC47LLL0iqrrJLDx5JLLpnGhKOOOir9+uuv9e6/6qqr0g477JD69u2bTjrppDRs2LB87Msvv3z+Mlk3xFSLEBlBKIJdfNlfdNFF85fW+DL82Wef5Taqa/Dgwfl427Ztm+6+++6awB5je6O9o6odfzyI0HLTTTflL/nxBblulfvKK69MQ4cOzWEgjivGB0ebvfHGGznIhgcffDBvK86Z+JIc2zjnnHPyeOKXX3655tgiGEc7x3Z23333NPfcc+dgGn84iPZYccUVczsVjjvuuPzzr3/9a819TZ2TjbV9Y6Ld4hwL0Y5xbGuvvXYOtBGmR+d1u//++/NrFW18+OGH5/tivHX1ubvddtuljTbaKG288cY190033XT5Z7Rj//798/IR1N9999283XgPxR9P4nUt/Pjjj3m/N99887TVVlulG2+8MT+nXbt2eR8aE38oinOjrni911hjjTTbbLOl6HgW69t1111zm2yyySZ5mQceeCB99NFHaaeddsohtOiqHz8jCEXgGRPiPL344oubtWy8VnFejkxLzrM4njiPZ5xxxvw6RjCP9thwww3TLbfckl+/hsQ5FJ9/cY7H8m3atGnWe3meeeaptW/RprFscZ6GCNvFvsW6IoQeeuih+Zy48MIL8/s7Po+XWmqpsfZ5CqQUH5AAjEGDBw+OIQ+VDTbYoNnPieXj9uKLL9bc9/HHH1cmn3zyykYbbVRz37Bhw+o995lnnsnPvfLKK2vuu+yyy/J9AwcOrLnvvffey/edfPLJNfftsMMOlSmnnLLeOm+66aa87COPPJJ/HzFiRGWOOeao9O3bN/+7en969epVWX311Wvu69evX37ut99+W2udL7zw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oFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpJpog2qpVq3T77benSUmfPn3S/vvvP653AwAAYOIKojvuuGMOmXFr27Zt6tatW1p99dXTpZdemkaMGFGz3JdffpnWWmutZq3z6KOPTgsvvHDN77///nvq2rVrOvHEExtc/phjjsnb/eOPP0brWOpud3Tdeuuted8AAAAmJON9EA1rrrlmDpqDBg1K99xzT1p55ZXTfvvtl9Zdd9303//+Ny/TvXv31L59+1Faf7t27dK2226bLrvssnqPVSqVdPnll6ftt98+B+HxQQTn0KVLl9SxY8extp3RDd4AAAATbBCNgBlBc8YZZ0yLLrpo+stf/pLuuOOOHEojJDbUNfezzz5LW221VQ5rU045ZVp88cXTc889l5fv379/eu2112oqrXHfLrvskt5777305JNP1tr2Y489lj766KP8eLjkkkvSPPPMkyaffPI099xzp/PPP7/W8i3dbvjkk0/SBhtskKaaaqrUqVOntPnmm6evv/66XiU1tt2rV6+87bpdcx999NGa9VbfoqJciDaL9ovn9+7dO+9PEeSLNrzgggvS+uuvn/f9uOOOG6OvIwAAQGgzoTbDKquskhZaaKHcPXXXXXet9djPP/+cVlpppRxc77zzzhxiX3755dyVd4sttkgDBgxI9957b3rwwQfz8p07d05TTDFFWmKJJXKX3+WXX75mXVElXXbZZXPovOaaa9JRRx2Vzj333LTIIoukV155Je222245tO2www6jtN14rAihEXojGO699955+QiXhQ8++CDdcsst+Xgnm2yyeu0R+xhV48Lbb7+d1l577bTiiivm35944olc1T377LPTCiuskD788MO0++6758f69etXK/RGF+UzzzwztWnT8Onx22+/5VthyJAho/AKAgAAk6oJNoiGCIevv/56vfuvvfba9O2336YXXnghVybD7LPPXvN4hL4IWREUq0XV8+CDD85hLZYZOnRouvnmm/PvRWA77bTT0sYbb5x/j+rkW2+9lS688MIcREdluw888EB644030sCBA1PPnj3zfVdeeWWab7758noiHBfdceP+6aabrtHuxcV6v//++xzOd95553wLUf08/PDD836GqIjG+NJDDz20VhDdeuut00477dRku59wwgl5fQAAABNt19zGxPjN6E5a16uvvporlkUYbK7oUjt8+PB044035t9vuOGG1Lp161yd/OWXX3IVMcJqBMriduyxx+b7R3W7UbmMAFqE0DDvvPOmqaeeOj9WmGWWWRoNoXXHdW6yySZ5+bPOOqvm/ugS/Pe//73Wvkc1N6qow4YNq1kuuhKPzBFHHJEGDx5cc/v000+bfbwAAAATdEU0glpUJeuKbrajIsZnbrrpprk7blQS42eM14zQVozZvPjii9NSSy1V63lFV9lR3W5zRPff5thzzz1zMHz++edrda2NbsNRxSyqudWKMafN3U6M2R3ViaEAAAAm2CD68MMP5y6tBxxwQL3HFlxwwTyxzw8//NBgdTK6sUblsyFR8YxJgO6666709NNPp1NOOSXfH5dv6dGjR564aJtttmnwuaOy3Zj4KIJj3IqqaHT3/emnn3JltCVOP/30XM2N/Z522mlrPRaTFL377ru1ugoDAACMCxNE19yYGOerr75Kn3/+eZ785/jjj88T/MTlW2ICnoa62MZ4yQ033DA99dRTOTzGRD/PPPNMfnzWWWfNYzKjK+13331Xa+KdmNwnwlqsN8agxiRAhagoxvjIGDMaM+xGEI6qaQTAUd3uaqutlhZYYIEcbuPYopIZ245Jj5rTTbYQEyDFeM8IznFN1GivuEXX2RCTLMUY0ziGN998M1eTr7/++vS3v/1tNF4ZAACAiTSIxkyzM8wwQw5ycU3RRx55JIfBuBxJQzPIRuXx/vvvT9NPP32eOTaCXswEWywbYyhjPXE90hh3ed1119U8N8acRrfcH3/8sWain0JMABQVzwifsc4Ii3EJlqJ78KhsN7YXxzHNNNPkEBzBNCYSivGpLRGXnYlq65/+9KfcVsUtrrca+vbtm6u8sX8xAdLSSy+dzjjjjDyWFAAAoEytKjHjD4yGuHxLXIqm5/43ptbtO4zr3QEAxmODTlxnXO8CUEI2iJ6ZMQfPBF0RBQAAYOIhiAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUqk25m2NiNqB/39SpU6dxvRsAAMB4TkUUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQqjblbo6J2fz97kut23cY17sBAACTjEEnrpMmRCqiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBNGJRKtWrdLtt98+rncDAABgwguiO+64Yw5VdW8ffPDBaK/78ssvT1NPPXUaHwJiHOeGG25Y6r4AAACMD9qk8dCaa66ZLrvsslr3TTfddGl88scff6S2bdumidnvv/+e2rVrN653AwAAmMiMdxXR0L59+9S9e/dat8kmmyzdcccdadFFF02TTz556t27d+rfv3/673//W/O8008/PS2wwAJpyimnTD179kx77bVX+vnnn/Njjz76aNppp53S4MGDa6qsRx99dKNVy6icRgU1DBo0KC9zww03pJVWWilv/5prrsmPXXLJJWmeeebJ980999zp/PPPH6Vjvvfee9Pyyy+ftzvttNOmddddN3344Ye1QuE+++yTZphhhrytWWaZJZ1wwgm11vHdd9+ljTbaKHXo0CHNMccc6c4776z1+IABA9Jaa62VpppqqtStW7e03Xbb5ecU+vTpk7ex//77p65du6a+ffuO0rEAAABMcEG0IU888UTafvvt03777ZfeeuutdOGFF+ageNxxx9Us07p163T22WenN998M11xxRXp4YcfToceemh+bNlll01nnnlm6tSpU/ryyy/z7eCDD27RPhx++OF5+2+//XYOaRFGjzrqqLwPcd/xxx+fjjzyyLztlvrll1/SgQcemF588cX00EMP5WOJUDlixIj8eBxXBMsbb7wxvfvuu3nbs846a611RDDffPPN0+uvv57WXnvttM0226QffvghP/bTTz+lVVZZJS2yyCJ5GxF8v/7667x8tdj3qII+9dRT6R//+EeD+/rbb7+lIUOG1LoBAABM0F1z77rrrly1K0QV78cff8xBcIcddsj3RUX0mGOOyUGzX79++b6o5BUipB177LHpT3/6U65SRrjq3LlzrmxGhXVUxPo33njjmt9ju6eddlrNfb169aoJycV+hq222ipXdOuGuXXWWafm90022aTW45deemnujhzrm3/++dMnn3ySq5xRNY1jiIpoQ+NOY1shQnGE1+effz53dT733HNzCI37q7cRleP33nsvzTnnnPm+2MbJJ5/cZDtEJTZCLwAAwEQTRFdeeeV0wQUX1PweXW0XXHDBXKWrroAOHz48/frrr2nYsGG5O+qDDz6YQ9I777yTq3TRbbf68dG1+OKL16pgRtfZXXbZJe22224198c2I/BWO+OMM9Jqq61W677DDjss73/h/fffz9XV5557LneXLSqhEUAjiEbIXH311dNcc82Vg2V03V1jjTVqrTPaqLrNovr7zTff5N9fe+219Mgjj9QK+IU4jiKILrbYYiNthyOOOCJXbwvR1hFoAQAAJtggGiFq9tlnr3VfjPWMKlx1RbIQYyZjHGeEsz333DOH1S5duqQnn3wyB8UYX9lUEI0KY6VSqTcZUUP7Vb0/4eKLL05LLbVUreXqVj+jAlv3eDp27Ji7yxbWW2+9XOWM9fXo0SMH0Qigse8hxsYOHDgw3XPPPTlwR5faCLc333xzzTrqTp4Ux1UE2tjf2MZJJ51U77hi3GlDx9jUGN64AQAATDRBtCERxGJsZN1AV3jppZdy6IqusjG+MsR4ymrRPbe6ClmILrAxZrS6OhlV1KbEZD8RGD/66KM8FnN0fP/99/nYIoSusMIK+b4I0XVFhXOLLbbIt0033TRXRmMMaITu5rTfLbfckrsst2kzwbzsAADARGiCSSTRbTUqnjPPPHMOYRE2o7tpzAQbY0EjoEYV85xzzsmVv4Ym24kQFpXBmAxooYUWylXSuMUkPjGGcplllslBNbrNNufSLFGh3XfffXNX3AiFMe4zJgKK8azVXVdHZppppskz5V500UW5OhndcWM8bLWYETgei3Gecew33XRTrrQ297qoe++9dw66MYY0xtVGeI1rs15//fV55t+6VVwAAIA0qc+aG7PUxiRG999/f1piiSXS0ksvncdeFpP2RLCMsBZdT6NLa8wqW/fyJjFzbkxeFBXFqIIWk/JEFTXGOEY1cuutt86z6TZnTOmuu+6aQ1xc8zQuGxOXdomZfGPSopaIYBmBMKq6se8HHHBAOuWUU+p15Y39jXGqcfzRFfnuu++uqf6OTFRvI5xH0I6xpbG/MflSBNnmrgMAAGBMaFWpOzgSWigmK4qqcM/9b0yt24/+pFAAAEDzDDrxf1fiGJ+yweDBg/PQwsYohQEAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVG3K3RwTswH9+6ZOnTqN690AAADGcyqiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlKpNuZtjYjZ/v/tS6/YdxvVuAAATsEEnrjOudwEogYooAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQXQC9uijj6ZWrVqln376aVzvCgAAwPgbRL/66qv05z//OfXu3Tu1b98+9ezZM6233nrpoYceKnU/IsDdfvvtNb+fdtppaZpppkm//vprvWWHDRuWOnXqlM4+++wxvt3Rseyyy6Yvv/wyde7ceYysDwAAYKILooMGDUqLLbZYevjhh9Mpp5yS3njjjXTvvfemlVdeOe29995pXNpuu+3SL7/8km699dZ6j918883p999/T9tuu20aX/zxxx+pXbt2qXv37jncjg1xzAAAABN0EN1rr71yaHr++efTJptskuacc84033zzpQMPPDA9++yzeZlPPvkkbbDBBmmqqabKVcjNN988ff311zXr2HHHHdOGG25Ya737779/6tOnT83v8e999903HXrooalLly45rB199NE1j88666z550YbbZT3J36ffvrpc2X20ksvrbffcV9sM9b16aef5n2aeuqp8++xrxGw6y4fxxUV3xlmmCHts88+jW63cMEFF6TZZpsth8u55porXXXVVbXWGcvHMuuvv36acsop03HHHVeva24cd/xe91bsXyy36667pummmy637SqrrJJee+21mm1EGy288MLpkksuSb169UqTTz55i15fAACA8SqI/vDDD7n6GZXPCFJ1RbAbMWJEDnax7GOPPZYeeOCB9NFHH6Utttiixdu74oor8naee+65dPLJJ6e///3veX3hhRdeyD8vu+yy3LW1+H2XXXbJ1dqPP/64Zj2x/ccffzw/FlXIvn37po4dO6YnnngiPfXUUzkwr7nmmjXVwwiLcYy77757rvjeeeedafbZZ29yu7fddlvab7/90kEHHZQGDBiQ9thjj7TTTjulRx55pNYxRVCMEBvr3Xnnnesdc1RzY73FbeONN86htlu3bvnxzTbbLH3zzTfpnnvuSS+99FJadNFF06qrrprbu/DBBx+kW265Ja/r1VdfbbBtf/vttzRkyJBaNwAAgOZqk0oSAadSqaS555670WVinGiErIEDB+axo+HKK6/M1cUIbUsssUSzt7fgggumfv365X/PMccc6dxzz83rX3311XNFsAi/US0tRMjs0aNHDopFBfXyyy/P+xKB7dprr81hOSqGRXfYWDbWE9XJNdZYIx177LE5UEawLBT73dh2Tz311FzpjYpxKCrEcX90Wy5svfXWOaBWh+RqUaEtnHHGGTlURxCfYoop0pNPPpkr0RFEo1JbbDfGq0bX4wjOIQJ1tHmxrw054YQTUv/+/Zv9WgAAAIyTimiE0JF5++23c+grQmiYd955c3CLx1oigmi16CIbIawpk002Wdphhx1y+Iz9jdAZldUIf61bt87dWCNQR0U0KqFxi/AXExx9+OGHef1ffPFFDq0tEce23HLL1bovfq97zIsvvniz1hcVz8MPPzzdcMMNuftziH3/+eef07TTTluz73GL0B/7XphlllmaDKHhiCOOSIMHD665RXdlAACA8a4iGlXJqCK+8847o7WeCIR1Q210ma2rbdu2tX6PbUewHJno8hoVv6gmxvIRsooqZAS5mGzpmmuuqfe8CG+xb2NTQ12a63rrrbfSlltumU488cRcoS3EvkcYj8ptXRH0W7KNqKgWVVUAAIDxNohG5TC6vp533nl5IqG6gScm0plnnnly8ItbURWNYBWPRWW0CHwxjrJajGWsGzxHJpYfPnx4vftjwqCVVlopTzgUgXe11VbLVcIQYyqjyhgTG8VkPw2JCYiiC3B1l9qRbTeOO8abRjW2EL8Xx9xc3333XZ5wKSaCOuCAA2o9Fvsel85p06ZNrUmSAAAAJupZcyOERghbcskl84Q477//fu5+GtfnXGaZZXLoW2CBBdI222yTXn755Tymcfvtt8/BsOiWGjO9vvjii3kcYzw/xoHWDabNUQTGCGc//vhjrcdiYqKYrCcmEYp/F2K/unbtmidUismKoltrVBgjWH/22Wd5mRhbGtckjWOK/YvjOOecc5rc7iGHHJK7A8dER/Gc008/PW//4IMPbtExRQDt0KFD3odYf3GLNo+2jTaO2X/vv//+PJPu008/nf7617/m9gQAAJgog2jv3r1zMItqYUzoM//88+fJgyKYRQiL7rN33HFHmmaaadKKK66Yw1M8J6qQhaiqHnnkkfnSLDEJ0NChQ3NYbakIizGLblReF1lkkXqBLrqeRqirvlRM/B4z6M4888x5RtqoZEZQjTGiRYU0qppnnnlmOv/88/MkS+uuu24Ol01tN7Zx1lln5cmD4jkXXnhhngSp+pI0zRH7FqE8KrjRDbe4RYU52vbuu+/O7RpdjWPsaHThjRmCi1l1AQAAytCq0pxZhKAJcfmWzp07p57735hat+8wrncHAJiADTpxnXG9C8AYyAYxqWljwxlLr4gCAACAIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQqjblbo6J2YD+fVOnTp3G9W4AAADjORVRAAAASiWIAgAAUCpBFAAAgFIJogAAAJRKEAUAAKBUgigAAAClEkQBAAAolSAKAABAqQRRAAAASiWIAgAAUCpBFAAAgFIJogAAAJRKEAUAAKBUgigAAAClEkQBAAAolSAKAABAqdqUuzkmZvP3uy+1bt9hXO8GAADjiUEnrjOud4HxlIooAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAADBpB9Edd9wxbbjhhk0uM+uss6YzzzwzTcoGDRqUWrVqlV599dVxvSsAAABjL4g2FhIfffTRHIp++umnVIYXXngh7b777mNsfbfcckuabLLJ0ueff97g43PMMUc68MADR3s7YzJA9+zZM3355Zdp/vnnHyPrAwAAmGQros0x3XTTpQ4dOoyx9a2//vpp2mmnTVdccUW9xx5//PH0wQcfpF122SWNL37//fccnLt3757atGkzVrYxfPjwNGLEiLGybgAAYNI2xoPo999/n7baaqs044wz5rC4wAILpOuuu67WMjfffHO+f4oppsgBcLXVVku//PJLrWVOPfXUNMMMM+TH99577/THH380WlmMauwll1ySNtpoo7zNqGDeeeedtdYXv8f9k08+eVp55ZVz6CyquG3btk3bbbdduvzyy+sdz6WXXpqWWmqpNN988+Vld9111xyEO3XqlFZZZZX02muv1Vr+X//6V1piiSXydrp27Zr3KfTp0yd9/PHH6YADDsjbjVt1RTbW3759+3xsp512Wq11xn3HHHNM2n777fN2oxpct2tuVKuL9Vbfolodfvvtt3TwwQfn12XKKafMx1Q8FuLYp5566txO8847b96XTz75pFmvOQAAwDgNor/++mtabLHF0r///e80YMCAHJoi5D3//PP58ehOGkF15513Tm+//XYOQxtvvHGqVCo163jkkUfShx9+mH9GYIyQ1FBIrNa/f/+0+eabp9dffz2tvfbaaZtttkk//PBDfmzgwIFp0003zd2KIzjuscce6a9//Wut50fF8/33388V0MLPP/+cQ3NRDd1ss83SN998k+6555700ksvpUUXXTStuuqqNduJY47gGdt/5ZVX0kMPPZSWXHLJ/Nitt96aZppppvT3v/89t0HcQqwn9nvLLbdMb7zxRjr66KPTkUceWe94I5gvtNBCeb3xeF1nnXVWzXrjtt9++6Xpp58+zT333PnxffbZJz3zzDPp+uuvz20Ux7LmmmvmYy4MGzYsnXTSSTnUv/nmm/n5DYlQO2TIkFo3AACA5mpVqU6AIxFVt6uvvjpX++p244wA+uOPP+aqWl3rrrtuDkQRpl5++eUcVKOiN8ssszS4jQinEUSj+2mIoNa6descoooK4f77759v+SBatUp/+9vfctUwRHV1qqmmyoExwtbhhx+eQ2IEvUIsf9xxx9Xa52WWWSbNNddcNSEwqqF//vOf01dffZUD7DrrrJODaFQLC7PPPns69NBDc+BedtllU+/evXMbNaTufocIzN9++226//77a+6L9cX+RhgsnrfIIouk2267rWaZaL9evXrlYLrwwgvX2k6E3ljvgw8+mJZbbrlc2Yz9ip89evSoWS4q0RGUjz/++HzMO+20U66wRuBtSoTlCP519dz/xtS6/ZjrMg0AwIRt0InrjOtdoGRRpOrcuXMaPHhw7s05xiqi0a01wkr1LSpo1aE0AmF0ve3SpUsOhPfdd19NN88IOVFFjMejKnfxxRfnMFgtuqkWITREF90IgE1ZcMEFa/4dXU/joIvnvPvuu7m7bLWiUlktqrRRAR06dGhNEI197NixYw6iUSGNrsJxTMUtqq0RmkO0RRxbS0RVOMJitfg9KpXRloXFF1+8WeuLYBoV6HPPPbdmvRHAY11zzjlnrX1/7LHHavY9tGvXrlY7NuaII47IJ1Zx+/TTT1twxAAAwKSuxTPdRMiLKmC1zz77rObfp5xySu4mGmM4I2zG8lEBjAl2QgTMBx54ID399NO5CnjOOefkbrLPPfdcrvCFGLNZLSqeI5s4Z1SeU1d0j40xnDfeeGNaccUV01NPPZVOOOGE/FiE0AjE1eMqC0VFNca8ji3RjiMTlduYeCnGsVZPrhT7Hu0e3YCrA36IQFqI/a8eu9qYqAhXV4UBAABaYoxPuRrhbYMNNkjbbrtt/j3C4HvvvZcnwClE2IlqXdyOOuqo3EU3up2OiUukNCS629599931LgFTV1Q+owIaldCoFEYFcYUVVsiPxXjQCHoxS210lW1IVBNjXGh0cW1IVByrq5xhnnnmyW1WLX6PbdcNjU2JrtHR7tEF+vTTT6/1WHTrje1Ghbg4HgAAgIlmsqKYmbaoeEa305gY6Ouvv655PCqfMSbxxRdfzN11YzxjjJGMQDa2xD6888476bDDDsuhOCqexTjQuhXAqCTGvv/jH//IXXWrx1PGGNKY8CgquTFGM5aLam4cS+jXr1+eITh+xrFHl9iY/KcQATYmQ4rrlX733Xf5voMOOiiH1+jOHPsWkzNFt9qY4balxxhdZM8+++zcnhGa4xaV6Ai1MWY0Zt2N9o7uxDF5VFR7YywqAADABB1EYxKgqB727ds3X7IkrnUZ4a0QYzcjjMXMshGQYvm4XMlaa62Vxpbo8htjPyOERdXyggsuqJk1t24X0+WXXz5XUGOQbQS3QgTWqKpGl92oeMa+R1feuCRLt27d8jJxvDfddFO+BEpMIBSXdylmCw4xY24E2Nlmmy1fAiZEW0UwjomY5p9//lwhjuVi0qaWiPGeMVtuVJ6jC3Fxi7AcLrvssnw8EXzj+OI1iarwzDPPPBotCwAAMJZnzZ2YxIy5UfU00c6YmxnLrLkAAFQza+6kZ0gzZ80d42NEx1fnn39+njk3Zr2NMZgxqVJcWxMAAIByTTJBNC6Hcuyxx6Yffvghd0eNLqpxGRIAAADKNckE0TPOOCPfAAAAmMgmKwIAAICmCKIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBSCaIAAACUShAFAACgVIIoAAAApRJEAQAAKJUgCgAAQKkEUQAAAEoliAIAAFAqQRQAAIBStSl3c0zMBvTvmzp16jSudwMAABjPqYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQqjblbo6J2fz97kut23cY17sBQEpp0InrjOtdAIBGqYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKWaIIPoU089lRZYYIHUtm3btOGGG6ZHH300tWrVKv3000+NPufyyy9PU089da37LrrootSzZ8/UunXrdOaZZ6YJTXOOGwAAYHwzXgXRCFVN3Y4++ui83IEHHpgWXnjhNHDgwBwwl1122fTll1+mzp07N3tbQ4YMSfvss0867LDD0ueff55++eWXNM0006Rff/213rLDhg1LnTp1SmefffYYOcbbb789jQmjctwAAADj2ngVRCNUFbeoUEb4q77v4IMPzst9+OGHaZVVVkkzzTRTrnK2a9cude/ePYe85vrkk0/SH3/8kdZZZ500wwwzpN122y2H0VtvvbXesjfffHP6/fff07bbbpvGF7Hvo3LcLRHHDAAAMFEH0QhVxS2qfBGwqu/77rvv8n3ff/992nnnnfO/oyLaUBfVuH/mmWdOHTp0SBtttFF+TvVj0bU39O7dOz83qp7rrbdeuvTSS+vtV9wXXYC7dOmSPv3007T55pvnABy/b7DBBmnQoEH1lp9vvvlS+/btc8iNymuYddZZ88/Yn9hm8Xu44IIL0myzzZbD5VxzzZWuuuqqWuuM5WOZ9ddfP0055ZTpuOOOq3fcffr0abCSXOxfLLfrrrum6aabLof8CPOvvfZazTai4hyV5ksuuST16tUrTT755KP5igIAAIznQXRkYjxnVEYjREXFNP69xRZb1FvuueeeS7vssksOgK+++mpaeeWV07HHHlvzeDznwQcfzP9+/vnn83pi3fGchx9+OH388cc1y3700Ufp8ccfz49FFbJv376pY8eO6YknnshjVaeaaqq05ppr1lQPIyzuvffeaffdd09vvPFGuvPOO9Pss8+eH3vhhRfyz8suuyxvs/j9tttuS/vtt1866KCD0oABA9Iee+yRdtppp/TII4/UOq4IihFiY70RxOuKam51BXnjjTfOobZbt2758c022yx988036Z577kkvvfRSWnTRRdOqq66afvjhh5p1fPDBB+mWW27J64q2a8hvv/2WuzZX3wAAAJqrTZqATDbZZDVdUaNiGv9uyFlnnZXD4aGHHpp/n3POOdPTTz+d7r333vz7FFNMkaaddtr876gOFuuJkNmjR48cFIvxqFE9jZAage3aa69NI0aMyBXDojtsLBvV0ahOrrHGGjnwRqCMYFlYYoklarYVYvnqfT/11FPTjjvumPbaa6+aMbDPPvtsvj9CdGHrrbfOAbU6JFeLCm3hjDPOyKE6Qnkc75NPPplDdwTRqNQW243xqtH1OIJziEB95ZVX1uxrQ0444YTUv3//Jl8rAACAiaIi2lxvv/12WmqppWrdt8wyyzQr6O6www45fFYqlRw6r7jiihz+Ymbd6MYaFcOoiEYlNG4R/mKCoxi3GiHviy++yKG1pfu73HLL1bovfo/7qy2++OLNWl9UPA8//PB0ww035BAeYt9//vnnHMCLfY9bTPgU+16YZZZZmgyh4YgjjkiDBw+uuUV3ZQAAgImyIlqG6PIaFb+oJkYQjZBVVCEjyC222GLpmmuuqfe8CG8RVsemGBs6Mm+99Vbacsst04knnpgrtIXY9xivGpXbuqova9OcbURFtaiqAgAAtNREGUTnmWee3CW1WnR1bY6YMGillVbKEw5FVXS11VbLVcIQYyqjyjj99NPncaoNiQmIHnrooVpdaqvFtU+HDx9eb39jvGlUYwvx+7zzzptaIiZzigmXNtlkk3TAAQfUeiz2/auvvkpt2rSpNUkSAABA2SbKrrn77rtvHg8aYyDff//9dO6559aMD22OmJgoJuuJSYTi34Vtttkmde3aNc+UG5MVRbfWqDDG9j777LO8TIwtPe200/I1R2PbL7/8cjrnnHPqBdUIhT/++GO+75BDDsndgWOio3jO6aefnrdfXK6muSKAxizBsQ+x/uIWwTcCdXRPjtl/77///jyTboyb/etf/5pefPHFFm0HAABgdEyUQXTppZdOF198cZ60aKGFFsrB629/+1uLAl10PY1QF8GtEL/HDLpxWZiYkTYqmRFUY4xoUSGNqmbM6Hv++efnS7isu+66OVwWIqQ+8MADeQKkRRZZJN8X24h9jeAcz7nwwgvzJEhxOZaWiH2LWXejghvdcItbdC+OyZXuvvvutOKKK+auxjF2NLrwxgzBxay6AAAAZWhVif6nMBri8i0xi3HP/W9Mrdt3GNe7A0BKadCJ64zrXQBgEs4GgwcPbnQ440RbEQUAAGD8JYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlEoQBQAAoFSCKAAAAKUSRAEAACiVIAoAAECpBFEAAABKJYgCAABQKkEUAACAUgmiAAAAlKpNuZtjYjagf9/UqVOncb0bAADAeE5FFAAAgFIJogAAAJRKEAUAAKBUgigAAAClEkQBAAAolSAKAABAqQRRAAAASiWIAgAAUCpBFAAAgFIJogAAAJRKEAUAAKBUgigAAAClEkQBAAAolSAKAABAqQRRAAAASiWIAgAAUCpBFAAAgFIJogAAAJRKEAUAAKBUbcrdHBOjSqWSfw4ZMmRc7woAADAOFZmgyAiNEUQZbd9//33+2bNnz3G9KwAAwHhg6NChqXPnzo0+Logy2rp06ZJ/fvLJJ02ebIz+X5ci7H/66aepU6dO43p3JkrauBzauRzauRzaeezTxuXQzuWYFNq5UqnkENqjR48mlxNEGW2tW//fUOMIoRPrG2p8Em2snccubVwO7VwO7VwO7Tz2aeNyaOdydJrI27k5xSmTFQEAAFAqQRQAAIBSCaKMtvbt26d+/frln4w92nns08bl0M7l0M7l0M5jnzYuh3Yuh3b+n1aVkc2rCwAAAGOQiigAAAClEkQBAAAolSAKAABAqQRRAAAASiWIUs95552XZp111jT55JOnpZZaKj3//PNNLn/TTTelueeeOy+/wAILpLvvvrvW4zEf1lFHHZVmmGGGNMUUU6TVVlstvf/++2lS15J2vvjii9MKK6yQpplmmnyLNqy7/I477phatWpV67bmmmumSV1L2vnyyy+v14bxvGrO59Fv5z59+tRr57its846Ncs4n2t7/PHH03rrrZd69OiR2+L2228f6XMeffTRtOiii+aZGWefffZ8fo/u5/3ErqXtfOutt6bVV189TTfddPnC9Msss0y67777ai1z9NFH1zuX4/+Zk7KWtnOcyw19Znz11Ve1lnM+j3obN/SZG7f55puvZhnncn0nnHBCWmKJJVLHjh3T9NNPnzbccMP07rvvjvR5vjv/H0GUWm644YZ04IEH5mmlX3755bTQQgulvn37pm+++abB5Z9++um01VZbpV122SW98sor+Q0YtwEDBtQsc/LJJ6ezzz47/eMf/0jPPfdcmnLKKfM6f/311zSpamk7x/+Eo50feeSR9Mwzz6SePXumNdZYI33++ee1losv6l9++WXN7brrrkuTspa2c4gvk9Vt+PHHH9d63Pk8+u0cX96r2zg+LyabbLK02Wab1VrO+fw/v/zyS27X+KLdHAMHDszBfuWVV06vvvpq2n///dOuu+5aKySNyvtjYtfSdo4v+xFE40vkSy+9lNs7vvzH/w+rxZf56nP5ySefTJOylrZzIb7gV7djfPEvOJ9Hr43POuusWm376aefpi5dutT7XHYu1/bYY4+lvffeOz377LPpgQceSH/88Uf+fhbt3xjfnavE5VugsOSSS1b23nvvmt+HDx9e6dGjR+WEE05ocPnNN9+8ss4669S6b6mllqrsscce+d8jRoyodO/evXLKKafUPP7TTz9V2rdvX7nuuusqk6qWtnNd//3vfysdO3asXHHFFTX37bDDDpUNNthgrOzvpNLOl112WaVz586Nrs/5PHbO5zPOOCOfzz///HPNfc7nxsX/um+77bYmlzn00EMr8803X637tthii0rfvn3H2Os2sWtOOzdk3nnnrfTv37/m9379+lUWWmihMbx3k1Y7P/LII3m5H3/8sdFlnM9j9lyO5Vu1alUZNGhQzX3O5ZH75ptvcns/9thjjS7ju/P/qIhS4/fff89/0Y3yf6F169b596jCNSTur14+xF9siuXjr/LRdaZ6mc6dO+cuM42tc2I3Ku1c17Bhw/Jf3eKvlXUrp/EX4rnmmivtueee6fvvv0+TqlFt559//jnNMsssueq8wQYbpDfffLPmMefz2Dmf//nPf6Ytt9wy/8W3mvN51I3ss3lMvG7UN2LEiDR06NB6n83RpS66SPbu3Ttts8026ZNPPhln+zghW3jhhXNXxahCP/XUUzX3O5/HvPhcjvaL/x9Wcy43bfDgwfln3c+Aar47/48gSo3vvvsuDR8+PHXr1q3W/fF73XEYhbi/qeWLny1Z58RuVNq5rsMOOyz/j6D6Qyq6MV555ZXpoYceSieddFLuLrLWWmvlbU2KRqWdI/Bceuml6Y477khXX311/lK57LLLps8++yw/7nwe8+dzjOGK7kjRbbSa83n0NPbZPGTIkPSf//xnjHwOUd+pp56a/5i1+eab19wXXx5jfO69996bLrjggvwlM8b8R2CleSJ8RhfFW265Jd/iD4Ux1jy64Abn85j1xRdfpHvuuafe57JzuWnxnSGGQSy33HJp/vnnb3Q5353/p03Vv4EJwIknnpiuv/76XC2qnkgnKkqFGPi+4IILptlmmy0vt+qqq46jvZ2wxEQjcStECJ1nnnnShRdemI455phxum8T81/d43xdcskla93vfGZCc+2116b+/fvnP2RVj12MP6AU4jyOL/NRZbrxxhvzGDFGLv5IGLfqz+YPP/wwnXHGGemqq64ap/s2MbriiivS1FNPncctVnMuNy3GisYfVif1cbMtoSJKja5du+YJQ77++uta98fv3bt3b/A5cX9Tyxc/W7LOid2otHP1X9sjiN5///35fwJNiW4zsa0PPvggTYpGp50Lbdu2TYssskhNGzqfx2w7x2QO8UeV5nyBmdTP55Zq7LM5JuOKGRjHxPuD/4nzOKpH8YW8bpe7uuIL/pxzzulcHk3xx6uiDZ3PY04MKY2eQdttt11q165dk8s6l/9nn332SXfddVeeVHKmmWZqclnfnf9HEKVGfOAstthiuStcdTeD+L26SlQt7q9ePsSsYcXyvXr1ym+a6mWia1jMANbYOid2o9LOxQxqUZWLLjGLL774SLcT3UljTF10aZoUjWo7V4uuXm+88UZNGzqfx2w7x/T1v/32W9p2221Hup1J/XxuqZF9No+J9wf/J2Zz3mmnnfLP6ksQNSa67kY1z7k8emI26KINnc9jTgyDiGDZnD8QOpf/L7hHCL3tttvSww8/nL8njIzvzlWqJi6CyvXXX59n5br88ssrb731VmX33XevTD311JWvvvoqP77ddttVDj/88Jrln3rqqUqbNm0qp556auXtt9/OM6q1bdu28sYbb9Qsc+KJJ+Z13HHHHZXXX389z4TZq1evyn/+85/KpKql7Rxt2K5du8rNN99c+fLLL2tuQ4cOzY/Hz4MPPrjyzDPPVAYOHFh58MEHK4suumhljjnmqPz666+VSVVL2zlmurzvvvsqH374YeWll16qbLnllpXJJ5+88uabb9Ys43we/XYuLL/88nkm17qcz5UG2+SVV17Jt/hf9+mnn57//fHHH+fHo32jnQsfffRRpUOHDpVDDjkkfzafd955lckmm6xy7733Nvt1mxS1tJ2vueaa/P/AaN/qz+aY4bJw0EEHVR599NF8Lsf/M1dbbbVK165d8+yak6qWtnPMrH377bdX3n///fz9Yr/99qu0bt06fzYUnM+j18aFbbfdNs/g2hDncn177rlnnm0/2qX6M2DYsGE1y/ju3DhBlHrOOeecyswzz5yDT0yH/uyzz9Y8ttJKK+XLKlS78cYbK3POOWdePi4X8O9//7vW4zEN9ZFHHlnp1q1b/p/EqquuWnn33Xcrk7qWtPMss8yS/0dS9xYfXiE+8NZYY43KdNNNlz/MYvnddtttkv0f8Ki28/7771+zbJyva6+9duXll1+utT7n85j53HjnnXfyOXz//ffXW5fzufHLV9S9Fe0aP6Od6z5n4YUXzq9J79698+WJWvK6TYpa2s7x76aWD/HHlhlmmCG38Ywzzph//+CDDyqTspa280knnVSZbbbZ8h8Gu3TpUunTp0/l4Ycfrrde5/PofWbEH1CmmGKKykUXXdTgOp3L9TXUxnGr/rz13blxreI/1RVSAAAAGJuMEQUAAKBUgigAAAClEkQBAAAolSAKAABAqQRRAAAASiWIAgAAUCpBFAAAgFIJogAAAJRKEAUAWmTQoEGpVatW6dVXX21yuXfffTd17949DR06NI0vfv/99zTrrLOmF198cVzvCsAkTRAFgPHQt99+m/bcc88088wzp/bt2+dA17dv3/TUU0+lCcURRxyR/vznP6eOHTvm3x999NEcYKeZZpr066+/1lr2hRdeyI/FrVAsX9ymmGKKNN9886WLLrqowe3ttNNO6W9/+1uT+9SuXbt08MEHp8MOO2yMHCMAo0YQBYDx0CabbJJeeeWVdMUVV6T33nsv3XnnnalPnz7p+++/TxOCTz75JN11111pxx13rPdYBNPbbrut1n3//Oc/c+hurLL65ZdfprfeeivtscceOaA/9NBDtZYZPnx43t76668/0n3bZptt0pNPPpnefPPNFh8XAGOGIAoA45mffvopPfHEE+mkk05KK6+8cpplllnSkksumSuM1UErqoQXXHBBWmuttXK1sHfv3unmm2+uta5PP/00bb755mnqqadOXbp0SRtssEHuWlvtkksuSfPMM0+afPLJ09xzz53OP//8Wo8///zzaZFFFsmPL7744jkgj8yNN96YFlpooTTjjDPWe2yHHXZIl156ac3v//nPf9L111+f72/I9NNPnyvCvXr1Svvuu2/++fLLL9da5umnn05t27ZNSyyxRO5+u88++6QZZpgh73O03wknnFCzbFRkl1tuubxNAMYNQRQAxjNTTTVVvt1+++3pt99+a3LZI488MldPX3vttVzp23LLLdPbb7+dH/vjjz9yd96oQEawjW69sd4111wzh7VwzTXXpKOOOiodd9xx+XnHH398XmdUYsPPP/+c1l133TTvvPOml156KR199NG5a+vIxPYitDZku+22y49H1TTccsstedzmoosu2uQ6K5VKuvfee/PzllpqqVqPRcV4vfXWy+H87LPPzr9HGI5qahxjrL9aBPvYBwDGDUEUAMYzbdq0SZdffnkOg1HJjOrdX/7yl/T666/XW3azzTZLu+66a5pzzjnTMccck8PfOeeckx+74YYb0ogRI3LFc4EFFshVz8suuywHuRh/Gfr165dOO+20tPHGG+dKY/w84IAD0oUXXpgfv/baa/M6outsjM+MUHrIIYeM9Bg+/vjj1KNHj0YrnFHFjWMMUR3deeedG13XTDPNlAN0jO9cZ5118j6vuOKKtZa54447aqrFcXxzzDFHWn755XM1NH5utdVWtZaPfYt9BGDcEEQBYDwUVc4vvvgiV/aighnBMSqGRXgrLLPMMvV+LyqiUSX94IMPckW0qLJG99yYKOjDDz9Mv/zyS/65yy671Dwet2OPPTbfH2JdCy64YO7i2tg2GxLdbaufU1cEzziWjz76KD3zzDO5mtuYqFzGDL1xi1AdVdvoklyIfYy2WnXVVfPvMS41lp1rrrlyV97777+/3jqjK/OwYcNGehwAjB1txtJ6AYDRFEFu9dVXz7foLhuVz6gGNjQBUEOiW+1iiy2Wu6bWNd100+XHw8UXX1yvq+tkk002WvvetWvX9OOPPzb6eFREd9999xyCo0vttNNO2+iyUamNynCIquxzzz2XuxLHpEUhwnq0URF8I7APHDgw3XPPPenBBx/MY2RXW221WuNnf/jhh9wGAIwbKqIAMIGIcZpRxaz27LPP1vs9uuAWgez999/PXWFnn332WrfOnTunbt265S6qUZWs+3iEvxDrii7B1ZdbqbvNhsTkRjHLbVPdj7fffvtc6W2qW25DIiRHxbW6W25MwlStU6dOaYsttsghO7ooxzjUCJ+FAQMG5H0EYNwQRAFgPBOXaFlllVXS1VdfnUNgVPduuummdPLJJ9cLXHF/jLGMS7xEtTRmuI0ZY0N0d43KZDwnurfGeiL4RXfVzz77LC/Tv3//PKNsTPAT63jjjTfyONLTTz89P7711lvnCYB22223HCzvvvvudOqpp470GGKSpOhyG5dVaUyMaY3rpcayTfnmm2/SV199lcd0xvFeddVVNe0Qj7344ot57Goh9v26665L77zzTj6meE7MultUVUO0xxprrDHS4wBg7NA1FwDGMzFOM7rKnnHGGXmsZsx+27NnzxwGY9KiahEk4zIke+21V75cSQSwqJyGDh06pMcffzwddthheRKioUOH5supxFjKqBiG6O4by51yyil5EqIpp5wyT2y0//771+zLv/71r/SnP/0pVxBj3XFZmRjD2pToehtVz+ga21jQjMmHIiiPTIz1DLG+aIe4lmjM3hti32IG3Or1xJjYCO1RDY7qaVzSJQJ069b/9/f3CMiDBw9Om2666Ui3DcDY0aoSc6EDABOcqFTedtttacMNN0zjo/POOy+P37zvvvvG2jZiptyYFffQQw9t9nOiy25c47RuqAegPCqiAMBYEZXLn376KVdio0o5NjR0aZamxPVTo+Ibl6gBYNxREQWACdT4XhEFgMaoiALABMrfkgGYUJk1FwAAgFIJogAAAJRKEAUAAKBUgigAAAClEkQBAAAolSAKAABAqQRRAAAASiWIAgAAkMr0/wCRglN64JfnPQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "fig, ax = plt.subplots(figsize=(10, 6))\n", + "y_pos = np.arange(len(vectorizer_stats[\"vectorizer\"]))\n", + "ax.barh(y_pos, vectorizer_stats[\"speed\"], align=\"center\")\n", + "ax.set_yticks(y_pos)\n", + "ax.set_yticklabels(vectorizer_stats[\"vectorizer\"])\n", + "ax.invert_yaxis()\n", + "ax.set_xlabel(\"Speed (MB/s)\")\n", + "ax.set_title(\"Сравнение скорости векторизации текстов\")\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "cf1deab5-fbe4-4bb9-af74-6fbca8958636", + "metadata": {}, + "source": [ + "Задание успешно выполнено. Самым быстрым оказался FeatureHasher, потому что он получил уже подготовленные словари, не делал никакой токенизации и разбора текста, а просто хешировал ключи - это очень быстро, тк это всего один проход по словарю." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/week4_scikit_learn.ipynb b/week4_scikit_learn.ipynb new file mode 100644 index 0000000..0fbe281 --- /dev/null +++ b/week4_scikit_learn.ipynb @@ -0,0 +1,487 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "3a40ada8-e64a-4c93-904c-b2a5a5c8ca70", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " 0 1.00 1.00 1.00 10\n", + " 1 1.00 0.75 0.86 12\n", + " 2 0.73 1.00 0.84 8\n", + "\n", + " accuracy 0.90 30\n", + " macro avg 0.91 0.92 0.90 30\n", + "weighted avg 0.93 0.90 0.90 30\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "D:\\4_week\\venv\\Lib\\site-packages\\sklearn\\neural_network\\_multilayer_perceptron.py:691: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (500) reached and the optimization hasn't converged yet.\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "from sklearn.datasets import load_iris\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.neural_network import MLPClassifier\n", + "from sklearn.metrics import classification_report\n", + "\n", + "# Загрузка и разбиение данных\n", + "X, y = load_iris(return_X_y=True)\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)\n", + "\n", + "# Модель MLP — многослойный перцептрон\n", + "clf = MLPClassifier(hidden_layer_sizes=(10,), activation='relu', max_iter=500)\n", + "clf.fit(X_train, y_train)\n", + "\n", + "# Отчёт о точности\n", + "print(classification_report(y_test, clf.predict(X_test)))" + ] + }, + { + "cell_type": "markdown", + "id": "daeaf6e9-855d-4f61-a062-2bc4aebc8ea2", + "metadata": {}, + "source": [ + "Целью является сравнение различных способов векторизации текстовых данных на примере подмножества новостных текстов из набора 20 Newsgroups. Анализируется эффективность методов по скорости и числу уникальных признаков." + ] + }, + { + "cell_type": "markdown", + "id": "befe0cc5-5c62-4a41-9f42-8aecb38c1d95", + "metadata": {}, + "source": [ + "1. Загрузка данных. Загружаем подмножество новостных текстов по выбранным категориям" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "5a6aa580-4e49-428c-9310-df95b84d7aea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading 20 newsgroups training data\n", + "3803 documents - 6.245MB\n" + ] + } + ], + "source": [ + "from sklearn.datasets import fetch_20newsgroups\n", + "\n", + "categories = [\n", + " \"alt.atheism\",\n", + " \"comp.graphics\",\n", + " \"comp.sys.ibm.pc.hardware\",\n", + " \"misc.forsale\",\n", + " \"rec.autos\",\n", + " \"sci.space\",\n", + " \"talk.religion.misc\",\n", + "]\n", + "\n", + "print(\"Loading 20 newsgroups training data\")\n", + "raw_data, _ = fetch_20newsgroups(subset=\"train\", categories=categories, return_X_y=True)\n", + "data_size_mb = sum(len(s.encode(\"utf-8\")) for s in raw_data) / 1e6\n", + "print(f\"{len(raw_data)} documents - {data_size_mb:.3f}MB\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "83f728a3-b22a-41e8-b51f-b8e7a5d55c95", + "metadata": {}, + "source": [ + "2. Предобработка: токенизация и частоты слов. Создадим простую функцию для разбиения текста на токены и подсчета частоты слов" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "e8466abd-87bc-4952-bd68-fdb4b4220bc5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "defaultdict(int,\n", + " {'that': 1,\n", + " 'is': 2,\n", + " 'one': 2,\n", + " 'example': 1,\n", + " 'but': 1,\n", + " 'this': 1,\n", + " 'another': 1})" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import re\n", + "\n", + "\n", + "def tokenize(doc):\n", + " \"\"\"Extract tokens from doc.\n", + "\n", + " This uses a simple regex that matches word characters to break strings\n", + " into tokens. For a more principled approach, see CountVectorizer or\n", + " TfidfVectorizer.\n", + " \"\"\"\n", + " return (tok.lower() for tok in re.findall(r\"\\w+\", doc))\n", + "\n", + "\n", + "list(tokenize(\"This is a simple example, isn't it?\"))\n", + "from collections import defaultdict\n", + "\n", + "\n", + "def token_freqs(doc):\n", + " \"\"\"Extract a dict mapping tokens from doc to their occurrences.\"\"\"\n", + "\n", + " freq = defaultdict(int)\n", + " for tok in tokenize(doc):\n", + " freq[tok] += 1\n", + " return freq\n", + "\n", + "\n", + "token_freqs(\"That is one example, but this is another one\")" + ] + }, + { + "cell_type": "markdown", + "id": "99797511-8c47-4b5f-aa06-c074c88ee277", + "metadata": {}, + "source": [ + "3. Векторизация с помощью DictVectorizer. Метод превращает словарь в разреженный числовой вектор" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "6901e1e7-fa59-459d-8df4-a0a6aaeac7c3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "done in 1.750 s at 3.6 MB/s\n", + "Found 47928 unique terms\n" + ] + } + ], + "source": [ + "from time import time\n", + "\n", + "from sklearn.feature_extraction import DictVectorizer\n", + "\n", + "dict_count_vectorizers = defaultdict(list)\n", + "\n", + "t0 = time()\n", + "vectorizer = DictVectorizer()\n", + "vectorizer.fit_transform(token_freqs(d) for d in raw_data)\n", + "duration = time() - t0\n", + "dict_count_vectorizers[\"vectorizer\"].append(\n", + " vectorizer.__class__.__name__ + \"\\non freq dicts\"\n", + ")\n", + "dict_count_vectorizers[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")\n", + "print(f\"Found {len(vectorizer.get_feature_names_out())} unique terms\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "37e02d00-0cb1-4bb9-a9ba-03978e578808", + "metadata": {}, + "source": [ + "4. Векторизация с помощью FeatureHasher. Метод применяет хеширование - каждому слову присваивается индекс с помощью хеш-функции" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "33b89a91-1f29-4edd-9aa0-997e00393ec7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "done in 0.947 s at 6.6 MB/s\n", + "Found 43873 unique tokens\n", + "done in 1.066 s at 5.9 MB/s\n", + "Found 47668 unique tokens\n", + "done in 0.909 s at 6.9 MB/s\n", + "Found 43873 unique tokens\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABCUAAAINCAYAAAAJJ8XzAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAP1FJREFUeJzt3QnclmPeP/6jRIl2S0UUGbJv2bcMQgxjfewRxjbEZBvzyL5ONKZHEmIeIcPIMtZsg7HvyZY0WbJThET9X9/j97/u575TSbiPu+73+/W6XnWd13md53Fe9z2m83N9j+/RYNq0adMSAAAAQC1rWNsnBAAAAAhCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCIalTkttWXq1Knp3XffTc2aNUsNGjQoPRwAAADmcdOmTUuff/55at++fWrYcNa1EEKJeVwEEh06dCg9DAAAAOqZt956Ky255JKz3EcoMY+LConKL0Pz5s1LDwcAAIB53MSJE/OX45X70VkRSszjKlM2IpAQSgAAAFBbZqeFgEaXAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAChCKAEAAAAUIZQAAAAAihBKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoIhGZU5LbVu5712pYeOmpYcBAHXa2HN6lB4CANQrKiUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAChCKAEAAAAUIZQAAAAAihBKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAAAAAIoQSgAAAABFCCUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAChCKAEAAAAUIZQAAAAAihBKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAAAAAObtUKJBgwZp+PDhqT7ZbLPNUu/evUsPAwAAAObNUKJnz545cIjH/PPPnxZffPG05ZZbpiuuuCJNnTq1ar/x48enbbbZZraOecopp6TVV1+96vk333yTFllkkXTOOefMcP/TTz89n3fKlCk/6VqmP+9P9Y9//COPDQAAAPiFKiW23nrrHDqMHTs23XHHHalbt27pqKOOStttt1369ttv8z5t27ZNjRs3nqPjL7DAAmnvvfdOQ4YM+d5r06ZNS1deeWXad999cyhSF0SIElq3bp2aNWv2i53np4YwAAAAMNeHEhE2ROiwxBJLpDXXXDP98Y9/TDfffHMOKCIwmNH0jbfffjvtscce+cZ9oYUWSmuvvXZ6/PHH8/6nnnpqev7556sqMGJbr1690muvvZYefvjhGud+8MEH05gxY/Lr4bLLLktdunRJTZo0SSussEK6+OKLa+z/Y88bxo0bl3bYYYe08MILp+bNm6fddtstvf/++9+rsIhzd+rUKZ97+ukbDzzwQNVxqz+i0qQiPrP4/OL9yyyzTB5PJdSpfIYDBw5Mv/nNb/LYzzzzzJ/jxwcAAABFNPqlDrz55pun1VZbLU9hOPDAA2u89sUXX6RNN900hxi33HJLDjSeeeaZPN1j9913TyNHjkx33nlnGjFiRN6/RYsWacEFF0xdu3bN00I22mijqmNF9cQGG2yQA4ihQ4emk08+OQ0YMCCtscYa6dlnn00HHXRQvoHfb7/95ui88VolkIgAJEKCww8/PO8fQUPF6NGj04033pivd7755vve5xFjjGqSipdffjltu+22aZNNNsnPH3rooVztcdFFF6WNN944vfHGG+nggw/Or/Xt27dGABLTWPr3758aNfrFfnwAAADwi/tF72ojKHjhhRe+t/2aa65JH374YXryySdzxULo3Llz1esRAMQNd4QG1UU1RJ8+ffKNe+zz+eefpxtuuCE/r9y89+vXL+200075eVQtjBo1Kg0aNCiHEnNy3nvuuSe9+OKL6c0330wdOnTI2/72t7+llVZaKR8ngpLKlI3Yvuiii850CkrluB9//HEOag444ID8CFEVccIJJ+RxhqiUiH4Uxx13XI1QYs8990z777//j/xJAAAAQD1bfSP6PcSUg+k999xzuZKhEgzMrph28d1336Xrr78+Px82bFhq2LBhrlqYNGlSri6I4CLChcrjjDPOyNvn9LxR0RBhRCWQCCuuuGJq2bJlfq1i6aWXnmkgMX0fiJ133jnv/5e//KVqe0wbOe2002qMPao8orriyy+/rNovppsAAADAvOAXrZSIm/aoVpheTMWYE9HPYZdddslTNqLCIP6M/g5xA1/p8TB48OC07rrr1nhfZTrFnJ53dsQUkdlx6KGHprfeeis98cQTNaZfxNSSqJaoVHlUV+lR8WPOAwAAAPU2lLjvvvvytIejjz76e6+tuuqquSnkJ598MsOqhZjqEBURMxKVENFA8rbbbkv//ve/0/nnn5+3x5Kg7du3z00v99prrxm+d07OG00zI0SIR6VaIqaEfPbZZ7li4se44IILcpVHjLtNmzY1XosGl6+++mqN6SQAAAAwL/tZpm9Mnjw5vffee+mdd97JjSPPOuus3BwylgSN5o0zmoYR/RV23HHH9Mgjj+QgIZpEPvroo/n1jh075h4OMd3io48+yseviMaQceMex42eFdFAsiIqDc4+++zcYyJW6ohQJKopIgyY0/NuscUWaZVVVslBR1xbVDjEuaNh5o+ZShHNM6M/RIQoiyyySP684jFhwoT8ejTojJ4UcQ0vvfRSrjK57rrr0p/+9Kef8JMBAACAeTyUiBUr2rVrl2/qt95663T//ffnYCCWuJzRShRRkXD33XenxRZbLK9AETf9saJEZd/ouRDH6datW+7TcO2111a9N3pUxNSNTz/9tKpJZEU0j4xKiAgi4pgRHMSynpUpJHNy3jhfXEerVq1yIBIhRTShjH4WP0YsZRpVGIccckj+rCqPo446Kr/evXv3XP0R44vmmeutt1668MILc+8JAAAAmBc1mBbdKJlnRHVH9cqSiRMn/r9Gnb2vTw0bNy06NgCo68ae06P0EABgrhf3oS1atMgzA6I3ZLHVN6h9MX0lfviVR/VVQwAAAKAuEUrMY0488cScRlUe0aATAAAA6t2SoNS+xo0b5wcAAADUdSolAAAAgCLm+lDilVdeyStVNGnSJK2++uqpLohVSPr371/1PFbwGD58eNExAQAAQF0z10/f6Nu3b1pooYXSq6++mhZeeOFUF40fPz4vKTo7TjnllBxgPPfcc7/4uAAAAKCkuT6UeOONN1KPHj3S0ksvPdN9pkyZkuaff/5UStu2bYudGwAAAFJ9n74xefLkdOSRR6bFFlssT7XYaKON0pNPPln1+gMPPJCnOdx7771p7bXXTk2bNk0bbLBBroCYmdj/6aefTqeddlr+e1QZjB07Nv992LBhadNNN83nGjp0aN7/sssuS126dMnbVlhhhXTxxRfXON4TTzyR1lhjjfx6jOGmm27Kx5pV1cIHH3yQtt9++7TgggumTp06VZ1r+nFWn77x9ttvpz322CO1bt06V3nEuR5//PF05ZVXplNPPTU9//zz+T3xiG3Tpk3L17bUUkvlJpbt27fPnyUAAADMzWqtUuK4445LN954Y7rqqqtyVcN5552XunfvnkaPHp1vzitOOumk1K9fv7ToooumQw45JB1wwAHpkUcemem0iC222CJtvfXWqU+fPnn6xkcffZRfO+GEE/JxKiFDhAUnn3xyGjBgQN727LPPpoMOOiiHAvvtt1/64osv0nbbbZe23HLLdPXVV6c333wzHXXUUT94XT179kzvvvtuuv/++3M1RoQFEVTMTJwnwpIlllgi3XLLLbmK4plnnklTp05Nu+++exo5cmS6884704gRI/L+LVq0yJ/bhRdemK677rq00korpffeey8HFwAAADA3q5VQYtKkSWngwIH5W/9tttkmbxs8eHC655570uWXX56OPfbYqn3PPPPMfNNeCRZiasbXX3+dg4XpxQ19o0aNchhRmSJRCSV69+6ddtpppxq9JyKkqGyLqoZRo0alQYMG5VDimmuuycFAjCfOFTf/UdFw6KGHzvS6XnvttXTHHXfkCouuXbvmbfH+qMaYmTjPhx9+mKtEKmFM586dq16Pa4lrqj7lY9y4cfl5BDARfETFxDrrrDMbnzwAAADU8+kb0fch+jpsuOGGVdvi5jpurF9++eUa+6666qpVf2/Xrl3+c1aVBzMTUyKqhyIxhl69euWb/srjjDPOyNtDjCPOXT38WH/99Wd5jnhPBAhrrbVW1baYFtKyZcuZviemgkSlRvXqkB+y6667pq+++iots8wyubojppV8++23s/1+AAAAqIvqXKPL6g0po6dCiAqGHyumZVSfMlGpzlh33XVr7DfffPOl2hS9J36sDh065N4aMaUjqksOO+ywdP7556cHH3ywaANPAAAAqPOVEssuu2xaYIEFavSGiMqJmMKw4oor/uLnX3zxxXNzyDFjxuSpEtUfMY0jxJSLF154IU8VqXjsscdmedyoioiKhWi2WRHh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vflaImCYOnVqfj2uK0KG66+/Pr366qv53B07dqxxjAhpdtttt/TCCy+kbbfdNu21117pk08+ya999tlnafPNN09rrLFGPkeEIO+//37ev7oYe1RHPPLII+mSSy750dcBAAAAc/X0jdtuuy1/m18R3+5/+umnORTYb7/98raolDj99NNz6NC3b9+8Lb7hr4gb9jPOOCMdcsghuXohbrRbtGiRKx6i8mJOxPF32mmnqudx3n79+lVt69SpU1VgUhln2GOPPXKlR3WTJ09OPXr0qHq+884713j9iiuuyFNW4ngrr7xyGjduXK5+iGqKuIaolJhRn4o4V4iAJIKMJ554Ik+HGTBgQA4kYnv1c0RFyWuvvZZ+9atf5W1xjvPOO2+OPh8AAACY60OJbt26pYEDB1Y9j+kYq666av72vnplxHfffZe+/vrr9OWXX+YpCyNGjMhTGl555ZU0ceLEPLWj+us/1dprr12jsiGmV/Tq1SsddNBBVdvjnBF+VHfhhRemLbbYosa2448/Po+/4vXXX89VF48//nieUlGpkIgwIkKJCBy23HLLtPzyy+eQIaZ3bLXVVjWOGZ9R9c8sqkI++OCD/Pz5559P999/f42wpyKuoxJKrLXWWnP8+QAAAMBcH0rEDXXnzp1rbIveEDE9oXqlQkX0WIi+D3Gjfuihh+bgonXr1unhhx/OoUH0Y5hVKBGVB9OmTfteI8sZjav6eMLgwYPTuuuuW2O/6asiojJj+utp1qxZnlJRsf322+fqhzhe+/btcygRYUSMPUQvjTfffDPdcccdOXyJaRcRdNxwww1Vx5i+8WZcVyXciPHGOc4999zvXVf0qZjRNQIAAEC9CyVmJG7Ko5fC9Df3FU8//XS+AY/pFNGPIUT/hepiCkf16oSKmCYRPSaqVy1EdcWsRKPICA/GjBmTezf8FB9//HG+tggkNt5447wtApXpReXD7rvvnh+77LJLrpiInhERwMzO53fjjTfmaS2NGv0sPxIAAACo836WO+CY2hCVEEsttVS+IY/gIaYkxIoS0TsiwoqobvjrX/+aKwJm1KgxbsijYiAaSa622mq5eiIe0QAyei6sv/76ObSIqRWzs9xnVG4ceeSRebpGBATRJyKaSEb/i2haObtatWqVV9y49NJLc9VCTNmI/hnVxcoi8Vr0hYhr//vf/54rMGK1jtlx+OGH59Ajek5EH44IMkaPHp2uu+66vILI9NUdAAAAMC/4WVbfiNUuogHm3Xffnbp27ZrWW2+93Kuh0vAxQoa4cY/pCTHtIVanmH7JzFiBIxpfRqVBVEdUGjpGdUU0fIwqhT333DOvyjE7PSgOPPDAfEM/ZMiQvBRpLBcaK4JEw8sfI0KGCAei2iPGfvTRR6fzzz//e9M9YrzR1yKuP6ar3H777VVVIT8kqjoiqInQJXpRxHijcWeEGrN7DAAAAJjbNJg2fcMG5mpRERKPimgqGqFOh97Xp4aNf3pDUQAAgPpi7Dn/tyojsy/uQ2PWwoQJE3Krg1nxNfw8JipQ4odfeUQgAQAAAHWRUGIec+KJJ+Y0qvJ46623Sg8JAAAAZshSD/OYxo0b5wcAAADUdSolAAAAgCLm+lDilVdeyat9NGnSJK2++uqpLojlTfv371/1vEGDBmn48OFFxwQAAAB1zVw/faNv375poYUWSq+++mpaeOGFU100fvz41KpVq9na95RTTskBxnPPPfeLjwsAAABKmutDiTfeeCP16NEjLb300jPdZ8qUKWn++edPpbRt27bYuQEAACDV9+kbkydPTkceeWRabLHF8lSLjTbaKD355JNVrz/wwAN5msO9996b1l577dS0adO0wQYb5AqImYn9n3766XTaaaflv0eVwdixY/Pfhw0bljbddNN8rqFDh+b9L7vsstSlS5e8bYUVVkgXX3xxjeM98cQTaY011sivxxhuuummfKxZVS188MEHafvtt08LLrhg6tSpU9W5ph9n9ekbb7/9dtpjjz1S69atc5VHnOvxxx9PV155ZTr11FPT888/n98Tj9g2bdq0fG1LLbVUbmLZvn37/FkCAADA3KzWKiWOO+64dOONN6arrroqVzWcd955qXv37mn06NH55rzipJNOSv369UuLLrpoOuSQQ9IBBxyQHnnkkZlOi9hiiy3S1ltvnfr06ZOnb3z00Uf5tRNOOCEfpxIyRFhw8sknpwEDBuRtzz77bDrooINyKLDffvulL774Im233XZpyy23TFdffXV6880301FHHfWD19WzZ8/07rvvpvvvvz9XY0RYEEHFzMR5IixZYokl0i233JKrKJ555pk0derUtPvuu6eRI0emO++8M40YMSLv36JFi/y5XXjhhem6665LK620UnrvvfdycAEAAABzs1oJJSZNmpQGDhyYv/XfZptt8rbBgwene+65J11++eXp2GOPrdr3zDPPzDftlWAhpmZ8/fXXOViYXtzQN2rUKIcRlSkSlVCid+/eaaeddqrReyJCisq2qGoYNWpUGjRoUA4lrrnmmhwMxHjiXHHzHxUNhx566Eyv67XXXkt33HFHrrDo2rVr3hbvj2qMmYnzfPjhh7lKpBLGdO7cuer1uJa4pupTPsaNG5efRwATwUdUTKyzzjqz8ckDAABAPZ++EX0foq/DhhtuWLUtbq7jxvrll1+use+qq65a9fd27drlP2dVeTAzMSWieigSY+jVq1e+6a88zjjjjLw9xDji3NXDj/XXX3+W54j3RICw1lprVW2LaSEtW7ac6XtiKkhUalSvDvkhu+66a/rqq6/SMsssk6s7YlrJt99+O9vvBwAAgLqozjW6rN6QMnoqhKhg+LFiWkb1KROV6ox11123xn7zzTdfqk3Re+LH6tChQ+6tEVM6orrksMMOS+eff3568MEHizbwBAAAgDpfKbHsssumBRZYoEZviKiciCkMK6644i9+/sUXXzw3hxwzZkyeKlH9EdM4Qky5eOGFF/JUkYrHHntslseNqoioWIhmmxURHnz22WczfU9UY0S1xCeffDLD1+Nz+u6772YYZkRDzYsuuig3BX300UfTiy++OFvXDwAAAPU2lIiqhejNEL0joolj9HKIaQhffvllnlJRG2JVi7PPPjvf1EcviLihHzJkSLrgggvy63vuuWeuzIhxxfhuv/329Oc//3mWx1x++eVzk83f/e53efWMCCcOPPDAWVZDxKob0R9ixx13zCFNBCXRyDJChtCxY8fcZDOCi+iPEauWRC+O6FURTTBj/2jEGeeY1TKoAAAAUNfV2pKg55xzTtp5553TPvvsk9Zcc8286sZdd92VWrVqVSvnj7AglgSNIGKVVVbJzTTjZr9SKRE9Jm699dYcVkTPh1gF5Nxzz/3B48bxogojjhdNNA8++OC87OnMRCXE3XffnffZdttt81jis6lMI4nPKIKObt265RVIrr322tyjIqaeRE+OqLSIaRwx1jZt2vyMnxAAAADUrgbTpk2bVsvnnGuMHTs2hxaxfOjqq6+e5kYTJ07My4p26H19ati4aenhAAAAzDXGntOj9BDm6vvQCRMmpObNm9eNSgkAAACA6oQSAAAAQBF1bknQuiSaTprdAgAAAL8MlRIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAAAAAIoQSgAAAABFCCUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAChCKAEAAAAUIZQAAAAAihBKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAAAAAIoQSgAAAABFCCUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARjcqclto28tTuqXnz5qWHAQAAAFVUSgAAAABFCCUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAChCKAEAAAAUIZQAAAAAihBKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAAAAAIoQSgAAAABFCCUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQRKMyp6W2rdz3rtSwcdPSwwAAAGAOjD2nR5oXqZQAAAAAihBKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAAAAAIoQSgAAAABFCCUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAChCKAEAAAAUIZQAAAAAihBKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAAAAAIoQSgAAAABFCCUAAACAIoQSAAAAQBFCCQAAAKAIoQQAAABQhFACAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAChCKFELGjRokIYPH156GAAAADD3hhI9e/bMN9jTP0aPHv2TB3LllVemli1bproQFsR17rjjjrU6FgAAAKhvGv3YN2y99dZpyJAhNbYtuuiiqS6ZMmVKmn/++dO87JtvvkkLLLBA6WEAAABA7U3faNy4cWrbtm2Nx3zzzZduvvnmtOaaa6YmTZqkZZZZJp166qnp22+/rXrfBRdckFZZZZW00EILpQ4dOqTDDjssffHFF/m1Bx54IO2///5pwoQJVdUXp5xyykyrGaKiIiorwtixY/M+w4YNS5tuumk+/9ChQ/Nrl112WerSpUvetsIKK6SLL754jj6kO++8M2200Ub5vG3atEnbbbddeuONN2oEBEcccURq165dPtfSSy+dzj777BrH+Oijj9Jvf/vb1LRp07TccsulW265pcbrI0eOTNtss01aeOGF0+KLL5722Wef/J6KzTbbLJ+jd+/eaZFFFkndu3efo2sBAACAeaqnxEMPPZT23XffdNRRR6VRo0alQYMG5dDgzDPP/L8TNWyYLrroovTSSy+lq666Kt13333puOOOy69tsMEGqX///ql58+Zp/Pjx+dGnT58fNYYTTjghn//ll1/ON+wRTJx88sl5DLHtrLPOSv/93/+dz/1jTZo0KR1zzDHpqaeeSvfee2++lggYpk6dml+P64qQ4frrr0+vvvpqPnfHjh1rHCNCmt122y298MILadttt0177bVX+uSTT/Jrn332Wdp8883TGmuskc8RIcj777+f968uxh7VEY888ki65JJLfvR1AAAAwFw9feO2227L3+ZXxLf7n376aQ4F9ttvv7wtKiVOP/30HDr07ds3b4tv+Cvihv2MM85IhxxySK5eiBvtFi1a5IqHqLyYE3H8nXbaqep5nLdfv35V2zp16lQVmFTGGfbYY49c6VHd5MmTU48ePaqe77zzzjVev+KKK/KUlTjeyiuvnMaNG5erH6KaIq4hKiVm1KcizhUiIIkg44knnsjTYQYMGJADidhe/RxRUfLaa6+lX/3qV3lbnOO8886bo88HAAAA5vpQolu3bmngwIFVz2M6xqqrrpq/va9eGfHdd9+lr7/+On355Zd5ysKIESPylIZXXnklTZw4MU/tqP76T7X22mvXqGyI6RW9evVKBx10UNX2OGeEH9VdeOGFaYsttqix7fjjj8/jr3j99ddz1cXjjz+ep1RUKiQijIhQIgKHLbfcMi2//PI5ZIjpHVtttVWNY8ZnVP0zi6qQDz74ID9//vnn0/33318j7KmI66iEEmuttdYcfz4AAAAw14cScUPduXPnGtuiN0RMT6heqVARPRai70PcqB966KE5uGjdunV6+OGHc2gQ/RhmFUpE5cG0adO+18hyRuOqPp4wePDgtO6669bYb/qqiKjMmP56mjVrlqdUVGy//fa5+iGO1759+xxKRBgRYw/RS+PNN99Md9xxRw5fYtpFBB033HBD1TGmb7wZ11UJN2K8cY5zzz33e9cVfSpmdI0AAABQ70KJGYmb8uilMP3NfcXTTz+db8BjOkX0YwjRf6G6mMJRvTqhIqZJRI+J6lULUV0xK9EoMsKDMWPG5N4NP8XHH3+cry0CiY033jhvi0BlelH5sPvuu+fHLrvskismomdEBDCz8/ndeOONeVpLo0Y/y48EAAAA6ryf5Q44pjZEJcRSSy2Vb8gjeIgpCbGiRPSOiLAiqhv++te/5oqAGTVqjBvyqBiIRpKrrbZarp6IRzSAjJ4L66+/fg4tYmrF7Cz3GZUbRx55ZJ6uEQFB9ImIJpLR/yKaVs6uVq1a5RU3Lr300ly1EFM2on9GdbGySLwWfSHi2v/+97/nCoxYrWN2HH744Tn0iJ4T0YcjgozRo0en6667Lq8gMn11BwAAAMwLfpbVN2K1i2iAeffdd6euXbum9dZbL/dqqDR8jJAhbtxjekJMe4jVKaZfMjNW4IjGl1FpENURlYaOUV0RDR+jSmHPPffMq3LMTg+KAw88MN/QDxkyJC9FGsuFxoog0fDyx4iQIcKBqPaIsR999NHp/PPP/950jxhv9LWI64/pKrfffntVVcgPiaqOCGoidIleFDHeaNwZocbsHgMAAADmNg2mTd+wgblaVITEoyKaikao06H39alh45/eUBQAAIDaN/ac/1shsq6L+9CYtTBhwoTc6mBWfA0/j4kKlPjhVx4RSAAAAEBdJJSYx5x44ok5jao83nrrrdJDAgAAgBmy1MM8pnHjxvkBAAAAdZ1KCQAAAKAIocTPoEGDBmn48OGlhwEAAABzFaFENcIFAAAAqD1zRSgxZcqU0kMAAAAA6lIoMXny5HTkkUemxRZbLDVp0iRttNFG6cknn6x6/YEHHsjVB/fee29ae+21U9OmTdMGG2yQXn311Zkec+zYsfk9w4YNS5tuumk+7tChQ9PHH3+c9thjj7TEEkvk46yyyirp2muvrXrfbbfdllq2bJm+++67/Py5557LxznhhBOq9jnwwAPT3nvvPcPzduzYMf/529/+Nr+v8jwMHDgwLbvssmmBBRZIyy+/fPrf//3fWX4uffv2Te3atUsvvPBCfv7www+njTfeOC244IJ5ic74zCZNmlTj3GeddVY64IADUrNmzdJSSy2VLr300qrXv/nmm3TEEUfkY8bnsfTSS+elPwEAAKDehhLHHXdcuvHGG9NVV12VnnnmmdS5c+fUvXv39Mknn9TY76STTkr9+vVLTz31VGrUqFG++f4hESYcddRR6eWXX87H/Prrr9Naa62V/vnPf6aRI0emgw8+OO2zzz7piSeeyPvHTf/nn3+enn322fz8wQcfTIssskgORipi22abbTbD81XClCFDhqTx48dXPb/pppvyOP7whz/k8/7ud79L+++/f7r//vu/d4xp06al3//+9+lvf/tbeuihh9Kqq66a3njjjbT11lunnXfeOYcUEbZESBEhQ3Xx+URwE+M/7LDD0qGHHloV3lx00UXplltuSddff33eFiFN9dAEAAAA5kYNpsWd9ByIb/pbtWqVrrzyyrTnnntWTbOIm+XevXunY489NgcC3bp1SyNGjEi//vWv8z6333576tGjR/rqq6/yt/4zqpTo1KlT6t+/fw4DZmW77bZLK6ywQvrzn/+cn0doEdUUffr0yRUPXbt2TaeeemquspgwYUJacskl02uvvZaWW265GX8YDRrkEGLHHXes2rbhhhumlVZaqUblwm677ZavPwKSyvv+/ve/5/dGqHDPPffkio5KdcZ8882XBg0aVPX+CCWiCiSOEZ9BfGYRqlQqMOJH0rZt2zz2Qw45JFdWvPTSS/lzjHP9GBMnTkwtWrRIHXpfnxo2bvqj3gsAAEDdMPacHmluUbkPjfvw5s2b/zKVElEBECFE3LRXzD///GmdddbJ1Q3VRcVARUxBCB988MEsjx9VA9XFtIzTTz89T9to3bp1WnjhhdNdd92Vxo0bV7VP3OhHEBI39VGpsNNOO6UuXbrkECCqJNq3bz/TQGJm4lqqX2OI59Nf49FHH50ef/zx9K9//asqkAjPP/98Dm5ivJVHVH5MnTo1vfnmmzP8jCJ4iFCi8hn17NkzT0eJqSMRUNx9990/6hoAAACg3ja6jLCiovJNf9yUz8pCCy1U4/n555+f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hNJUSsyjvvnmm/zN2BZbbFG1rWHDhvn5o48+WnRsQBkTJkyouiml/vnuu+/SddddlytlYhoH9UtUTfXo0aPGvwuoP+LGI6ZyLrPMMmmvvfZK48aNKz0katEtt9yS1l577fyteHw5scYaa6TBgweXHhYF7xOvvvrqdMABB9SJQCIIJeZRH330Uf4H6OKLL15jezx/7733io0LKCMqpSIVj/LNlVdeufRwqEUvvvhiWnjhhVPjxo3TIYcckiumVlxxxdLDohZFGBXTOKPPDPWzcvbKK69Md955Z+4z8+abb6aNN944ff7556WHRi0ZM2ZM/tkvt9xy6a677kqHHnpoOvLII9NVV11VemgUMHz48PTZZ5+lnj17prqiUekBAFA735KOHDnSPOJ6aPnll89l21Epc8MNN6T99tsvT+ERTNQPb731VjrqqKPy/OFoek39s80221T9PfqJREix9NJLp+uvv940rnr0xURUSpx11ln5eVRKxL8JLrnkkvz/CdQvl19+ef7vQlRP1RUqJeZRiyyySJpvvvnS+++/X2N7PG/btm2xcQG174gjjsjzBu+///7c+JD6ZYEFFkidO3dOa621Vv6mPJrf/uUvfyk9LGpJTOWMBtfRT6JRo0b5EaHURRddlP8eVZXULy1btky/+tWv0ujRo0sPhVrSrl277wXRXbp0MY2nHvrPf/6TRowYkQ488MBUlwgl5uF/hMY/QO+9994aKWk8N5cY6ofoYxyBRJTr33fffalTp06lh0QdEP9fMHny5NLDoJb8+te/zlN4olqm8ohvTKOvQPw9vsCg/jU9feONN/KNKvVDTN2cfknw1157LVfMUL8MGTIk9xWJHkN1iekb87BYDjRKsuIfH+uss07ush0Nzvbff//SQ6MW/+FR/ZuQmEca/wiNRodLLbVU0bFRO1M2rrnmmnTzzTenZs2aVfWTadGiRV6jnHnfiSeemEs043/vMX88fh8eeOCBPKeY+iH+tz99H5mFFlootWnTRn+ZeqJPnz5p++23zzeg7777bl4qPsKoPfbYo/TQqCVHH3102mCDDfL0jd122y098cQT6dJLL80P6teXEkOGDMn3h1EpV5fUrdHws9p9993zEoAnn3xyvhmJ5b+iydH0zS+Zdz311FOpW7duNYKqEP8xiqZXzNuiqVXYbLPNamyP/0OqS82N+OVE2f6+++6b1ySPMCrmk0cgseWWW5YeGlBL3n777RxAfPzxx2nRRRdNG220UXrsscfy36kfunbtmqsmI6g+7bTTcuVkfFkZFVPUHyNGjMhTdmLVjbqmwbSo7wUAAACoZXpKAAAAAEUIJQAAAIAihBIAAABAEUIJAAAAoAihBAAAAFCEUAIAAAAoQigBAAAAFCGUAADqvc022yz17t37B/fbZJNN0jXXXJPqkhNOOCH9/ve/Lz0MAJgjQgkAgNlwyy23pPfffz/913/9V9W2jh07pgYNGqTrrrvue/uvtNJK+bUrr7zye/vHY7755kvt27dPvXr1Sp9++un33v/ggw+mDh06/OC4+vTpk6666qo0ZsyYn3R9AFCCUAIAYDZcdNFFaf/9908NG9b851MEB0OGDKmx7bHHHkvvvfdeWmihhb53nNNOOy2NHz8+jRs3Lg0dOjT961//SkceeeT39rv55pvT9ttv/4PjWmSRRVL37t3TwIED5+i6AKAkoQQAUGtuuOGGtMoqq6QFF1wwtWnTJm2xxRZp0qRJ+bWePXumHXfcMZ166qlp0UUXTc2bN0+HHHJI+uabb6reP3Xq1HT22WenTp065WOsttpq+ZjVjRw5Mm2zzTZp4YUXTosvvnjaZ5990kcffVT1epxv3333za+3a9cu9evX7wfH/eGHH6b77rtvhiHBXnvtlasa3nrrraptV1xxRd7eqFGj7+3frFmz1LZt27TEEkukbt26pf322y8988wzM6zM+M1vfvODn1uIcc2oWgMA6jqhBABQK6I6YI899kgHHHBAevnll9MDDzyQdtpppzRt2rSqfe69996q16699tr0j3/8I4cUFRFI/O1vf0uXXHJJeumll9LRRx+d9t577xwKhM8++yxtvvnmaY011khPPfVUuvPOO/OUi912263qGMcee2zePyoR7r777nyuGYUC1T388MOpadOmqUuXLt97LYKPqFSIKRThyy+/TMOGDcvX+UPeeeeddOutt6Z11123xva4tg8++CBfy+x8buuss056++2309ixY3/wnABQl3w/vgcA+AXEzfW3336bb6iXXnrpvC2+/a9ugQUWyFUGEQBET4aY6hAhwumnn56mTJmSzjrrrDRixIi0/vrr5/2XWWaZHBgMGjQobbrppmnAgAE5kIj9KuJ4McXitddeyz0cLr/88nT11VenX//61/n1CBOWXHLJWY79P//5Tw4fpp+6URGBwR/+8Id00kkn5aqGZZddNq2++uoz3Pf4449Pf/rTn9J3332Xvv766xxIXHDBBTX2icAkgo74PGbnc4vrqowz+lYAwNxCpQQAUCtiqkUEAXFDveuuu6bBgwd/r8Fj7BOBREWED1988UWeGjF69OhchbDlllvmqReVR1ROvPHGG3n/559/Pt1///01Xl9hhRXya7FPPGI6SPXKhNatW6fll19+lmP/6quvUpMmTWb6eo8ePfI4oz9EhCCzqpKIkOW5555LL7zwQq4Mqbw/QorqoURl6sbsfG4xrSPE5wMAcxOVEgBArYjVJu65557073//O0+b+Otf/5orCx5//PHcI+KHxE1/+Oc//5n7MVTXuHHjqn2iv8K55577vfdH/4gINuZENJOc0QoZFdE7InpX9O3bN1/PTTfdNMtjde7cOf99ueWWS/3798/hS4Qp0SsiKiOeffbZHFTM7uf2ySef5D+jFwcAzE1USgAAtSaWwtxwww1zn4i48Y7pCdVv4KPSIaoSqq9iEdUOMf1ixRVXzOFDrFoRN/XVH5WlM9dcc83cjyGmMEy/T6yEEdMq5p9//nxDXxFhQ0ztmJWYEhKracwqmIjqiOhVscMOO6RWrVrN9mcSoUOoXHf0mNhggw1yBcfsfm7R3DOuK6a8AMDcRKUEAFArIgiI6QpbbbVVWmyxxfLzWNWievPImFrRq1ev3HMhmjZG5cERRxyReznEqhV9+vTJzS1jFY6NNtooTZgwIT3yyCN5pY5YxeLwww/P0xuiMeRxxx2Xb+yjOiJWprjssstywBHHjykUsYpFjCOqDmbWK6J6KBEVDnGu7bbbbob7xHXEKh/Vp5/MyOeff54DjmhUGdNSYpxR4RBBxPSrbszu5/bQQw+ljTfeuGoaBwDMLYQSAECtiOAgei7EdIWJEyfmpo2xHGcs31kRvRNiSsMmm2ySJk+enMOFU045per1aHgZN/CxCseYMWNSy5Ytc3XEH//4x6qGjxEcRDPJuImPY8R5tt5666rg4fzzz6+a5hFBRzSojHDjh6oZ9t9//zR06NCZhhIhgo4fcvLJJ+dHiGvp2rVrnpYR741lPiOAiM/ox3xuEbpU/5wAYG7RYFr19aQAAArp2bNnXtJz+PDhqS6K6oaYHhHLh1ZWwfi5xRKoUSUyatSo2X7PHXfckYOVaJwZvS0AYG6ipwQAwGxo27ZtXk40elr8UmJ6yYyadM5KVFcMGTJEIAHAXEmlBABQJ9T1SgkA4OcnlAAAAACKMH0DAAAAKEIoAQAAABQhlAAAAACKEEoAAAAARQglAAAAgCKEEgAAAEARQgkAAACgCKEEAAAAUIRQAgAAAEgl/H9ssMN2W/TSzQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "\n", + "def n_nonzero_columns(X):\n", + " \"\"\"Number of columns with at least one non-zero value in a CSR matrix.\n", + "\n", + " This is useful to count the number of features columns that are effectively\n", + " active when using the FeatureHasher.\n", + " \"\"\"\n", + " return len(np.unique(X.nonzero()[1]))\n", + "from sklearn.feature_extraction import FeatureHasher\n", + "\n", + "t0 = time()\n", + "hasher = FeatureHasher(n_features=2**18)\n", + "X = hasher.transform(token_freqs(d) for d in raw_data)\n", + "duration = time() - t0\n", + "dict_count_vectorizers[\"vectorizer\"].append(\n", + " hasher.__class__.__name__ + \"\\non freq dicts\"\n", + ")\n", + "dict_count_vectorizers[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")\n", + "print(f\"Found {n_nonzero_columns(X)} unique tokens\")\n", + "t0 = time()\n", + "hasher = FeatureHasher(n_features=2**22)\n", + "X = hasher.transform(token_freqs(d) for d in raw_data)\n", + "duration = time() - t0\n", + "\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")\n", + "print(f\"Found {n_nonzero_columns(X)} unique tokens\")\n", + "t0 = time()\n", + "hasher = FeatureHasher(n_features=2**18, input_type=\"string\")\n", + "X = hasher.transform(tokenize(d) for d in raw_data)\n", + "duration = time() - t0\n", + "dict_count_vectorizers[\"vectorizer\"].append(\n", + " hasher.__class__.__name__ + \"\\non raw tokens\"\n", + ")\n", + "dict_count_vectorizers[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")\n", + "print(f\"Found {n_nonzero_columns(X)} unique tokens\")\n", + "import matplotlib.pyplot as plt\n", + "\n", + "fig, ax = plt.subplots(figsize=(12, 6))\n", + "\n", + "y_pos = np.arange(len(dict_count_vectorizers[\"vectorizer\"]))\n", + "ax.barh(y_pos, dict_count_vectorizers[\"speed\"], align=\"center\")\n", + "ax.set_yticks(y_pos)\n", + "ax.set_yticklabels(dict_count_vectorizers[\"vectorizer\"])\n", + "ax.invert_yaxis()\n", + "_ = ax.set_xlabel(\"speed (MB/s)\")" + ] + }, + { + "cell_type": "markdown", + "id": "83e7b545-4dc9-45c2-b5f4-31c2133d7d55", + "metadata": {}, + "source": [ + "5. Сравнение с CountVectorizer. Метод представляет из себя токенизацию и частоты слов" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "4cc0ea45-8784-400b-881a-312394c0335e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "done in 1.135 s at 5.5 MB/s\n", + "Found 47885 unique terms\n", + "done in 0.868 s at 7.2 MB/s\n" + ] + } + ], + "source": [ + "from sklearn.feature_extraction.text import CountVectorizer\n", + "\n", + "t0 = time()\n", + "vectorizer = CountVectorizer()\n", + "vectorizer.fit_transform(raw_data)\n", + "duration = time() - t0\n", + "dict_count_vectorizers[\"vectorizer\"].append(vectorizer.__class__.__name__)\n", + "dict_count_vectorizers[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")\n", + "print(f\"Found {len(vectorizer.get_feature_names_out())} unique terms\")\n", + "from sklearn.feature_extraction.text import HashingVectorizer\n", + "\n", + "t0 = time()\n", + "vectorizer = HashingVectorizer(n_features=2**18)\n", + "vectorizer.fit_transform(raw_data)\n", + "duration = time() - t0\n", + "dict_count_vectorizers[\"vectorizer\"].append(vectorizer.__class__.__name__)\n", + "dict_count_vectorizers[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")" + ] + }, + { + "cell_type": "markdown", + "id": "1bd2ed6d-508d-42e1-a8e4-6344b6ff493e", + "metadata": {}, + "source": [ + "6. HashingVectorizer. Комбинация CountVectorizer и FeatureHasher" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "e8ed6734-a8fe-41ee-b3a3-cbb06e0e1752", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "done in 1.030 s at 6.1 MB/s\n" + ] + } + ], + "source": [ + "from sklearn.feature_extraction.text import HashingVectorizer\n", + "\n", + "t0 = time()\n", + "vectorizer = HashingVectorizer(n_features=2**18)\n", + "vectorizer.fit_transform(raw_data)\n", + "duration = time() - t0\n", + "dict_count_vectorizers[\"vectorizer\"].append(vectorizer.__class__.__name__)\n", + "dict_count_vectorizers[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "8e3bb2ef-af88-488d-850a-154951863d53", + "metadata": {}, + "source": [ + "7. TF-IDF Vectorizer. Преобразуем частоты слов с учетом их значимости в коллекции документов" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "756d27ac-f26f-421b-82ef-a6a60dbf90af", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "done in 1.334 s at 4.7 MB/s\n", + "Found 47885 unique terms\n" + ] + } + ], + "source": [ + "from sklearn.feature_extraction.text import TfidfVectorizer\n", + "\n", + "t0 = time()\n", + "vectorizer = TfidfVectorizer()\n", + "vectorizer.fit_transform(raw_data)\n", + "duration = time() - t0\n", + "dict_count_vectorizers[\"vectorizer\"].append(vectorizer.__class__.__name__)\n", + "dict_count_vectorizers[\"speed\"].append(data_size_mb / duration)\n", + "print(f\"done in {duration:.3f} s at {data_size_mb / duration:.1f} MB/s\")\n", + "print(f\"Found {len(vectorizer.get_feature_names_out())} unique terms\")" + ] + }, + { + "cell_type": "markdown", + "id": "ecd038f0-c024-4a0b-a7fc-bcf648eed2f6", + "metadata": {}, + "source": [ + "8. Визуализация. Сравним производительность (MB/s) разных подходов" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "391b055b-fa67-4d43-903a-e7ff512b69c2", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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oEFp8cUXT4ceemg64IAD0tNPPz3daSPdunVLPXr0SP369cvTW7755pu87aSTTsrHKYUYEUacdtppaejQofmzV155JR100EE5dNhvv/3Sjz/+mLbZZpu0xRZbpOuuuy599NFHqU+fPjO9rl69eqXPP/88Pfroo7maJMKICEKmJ84TYczSSy+dRowYkatAXn755TR58uS0++67p1GjRqX77rsvPfTQQ3n/Jk2a5Pt28cUXp5tuuimtssoq6YsvvsjBCAAAANRkhQg9Jk2alIYNG5arFrbaaqv82ZVXXpkefPDB9Le//S0df/zxZfueffbZORQoBRcxdeXnn3/OwUVlERjUqVMnhx2lKSSl0KNv375pp512qtD7I0KQ0mdRlfHWW2+lK664IoceN9xwQw4eYjxxrggXoiLjsMMOm+51vffee+nee+/NFSJdunTJn8X3o5pkeuI8X3/9da5yKYU9HTp0KNse1xLXVH5KzNixY/P7CHgiWImKj3XWWWcW7jwAAABUX7WL0ncj+mpssMEGZZ/Fw3s8uL/99tsV9l199dXLfl5qqaXynzOqnJiemDJSPnSJMfTu3TuHCqXXWWedlT8PMY44d/lwZb311pvhOeI7EVCstdZaZZ/FtJmmTZtO9zsxVSYqTcpXt8zMrrvumv7zn/+k9u3b5+qUmHbz3//+d5a/DwAAANVRISo9Zkf5hqPR0yJEBcbsimkr5aeUlKpL1l133Qr7LbTQQml+it4fs6t169a5t0lMeYnqmMMPPzxdcMEF6fHHH6/SBq0AAACQFvRKj+WWWy4tvPDCFXpzROVHTPFYeeWV5/n5l1xyydz888MPP8xTScq/YppLiCkpr7/+ep5KU/Lcc8/N8LhR1REVF9FMtSTCie+//36634lqkqj2GD9+/DS3x336/fffpxmWRMPUIUOG5Kavzz77bHrjjTdm6foBAACgOipE6BFVF9EbI3p3RJPO6KUR0zR++umnPOVkfohVUc4999wcGkQvjggMhg8fni666KK8fc8998yVJTGuGN8999yTLrzwwhkec8UVV8xNVA855JC8+kqEHwceeOAMqzli1Zboz7HDDjvkECiCmGhUGiFGaNu2bW6iGsFI9CeJVW+iF0r0Cokmp7F/NFqNc8xomV4AAACo7goReoSBAwemnXfeOe2zzz6pc+fOedWW+++/PzVr1my+nD/CiFiyNoKO1VZbLTdLjTChVOkRPT7uvvvuHIZEz41YRea8886b6XHjeFFFEseLJqkHH3xwXpZ3eqKS44EHHsj7bL311nkscW9K02ziHkWQ0rVr17yCzY033ph7hMTUnOiJEpUiMc0lxtqiRYu5eIcAAABg/qo1ZcqUKfP5nPyfMWPG5FAklrddc801U001ceLEvPRt6763pNr1GlT1cABgrhgzsGdVDwEAmMlz6IQJE1Ljxo2LX+kBAAAAUJ7QAwAAACikBW7J2uokmoqaXQQAAADzhkoPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJIla5lrRg3onho3blzVwwAAAIBMpQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAh1anqAVAcq/a/P9Wu16CqhwEAAFDBmIE9q3oIVBGVHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSehRELVq1Up33nlnVQ8DAAAAqo1qF3r06tUrP8BXfo0ePfoPH/uaa65JTZs2TdUhjIjr3GGHHebrWAAAAGBBUidVQz169EjDhw+v8Nniiy+eqpPffvst1a1bNxXZr7/+mhZeeOGqHgYAAAAUo9Ij1KtXL7Vs2bLCa6GFFkp33XVX6ty5c6pfv35q3759GjBgQPrvf/9b9r2LLroorbbaaqlhw4apdevW6fDDD08//vhj3vbYY4+l/fffP02YMKGseuT000+fbjVGVIREZUgYM2ZM3ufmm29Om2yyST7/9ddfn7ddddVVqWPHjvmzlVZaKV122WVzdM333Xdf2nDDDfN5W7RokbbZZpv0wQcfVAggjjzyyLTUUkvlc7Vp0yade+65FY7xzTffpB133DE1aNAgLb/88mnEiBEVto8aNSpttdVWadFFF01LLrlk2mefffJ3SjbddNN8jr59+6bFFlssde/efY6uBQAAAKqDahl6TMuTTz6Z9t1339SnT5/01ltvpSuuuCKHEmeffXbZPrVr105DhgxJb775Zrr22mvTI488kk444YS8bf3110+DBw9OjRs3TuPGjcuvfv36zdYYTjrppHz+t99+OwcCEXycdtppeQzx2TnnnJP+8pe/5HPPrkmTJqVjjz02vfjii+nhhx/O1xIBxuTJk/P2uK4IMW655Zb07rvv5nO3bdu2wjEiBNptt93S66+/nrbeeuu01157pfHjx+dt33//fdpss81Sp06d8jkiZPnyyy/z/uXF2KO64+mnn06XX375bF8HAAAAVBfVcnrLv/71r1yNUBLVCd99910OHfbbb7/8WVR6nHnmmTnU6N+/f/4sKhRKIhA466yz0qGHHpqrL+JBvkmTJrliIypH5kQcf6eddip7H+cdNGhQ2Wft2rUrC2RK4wx77LFHrlQp75dffkk9e/Yse7/zzjtX2H711VfnKT1xvFVXXTWNHTs2V29ENUhcQ1R6TKtPSJwrRAATQcnIkSPzdKGhQ4fmwCM+L3+OqIh577330gorrJA/i3Ocf/75c3R/AAAAoDqplqFH165d07Bhw8rex3SV1VdfPVcflK/s+P3339PPP/+cfvrppzyl46GHHspTPt555500ceLEPPWl/PY/au21165QmRHTT3r37p0OOuigss/jnBGulHfxxRenbt26VfjsxBNPzOMvef/993PVyPPPP5+nnJQqPCLsiNAjAo0tttgirbjiijnEiOkvW265ZYVjxj0qf8+iquWrr77K71977bX06KOPVgiTSuI6SqHHWmutNcf3BwAAAKqTahl6xAN7hw4dKnwWvTli+kb5SouS6HERfTciCDjssMNyMNK8efP01FNP5VAi+mHMKPSIyokpU6ZM1ah0WuMqP55w5ZVXpnXXXbfCfpWrOqKypPL1NGrUKE85Kdl2221z9UYcr1WrVjn0iLAjxh6il8lHH32U7r333hzuxLSUCFJuvfXWsmNUbqwa11UKT2K8cY7zzjtvquuKPiHTukYAAACoyapl6DEt8dAfvSwqhwclL730Un7Aj+km0Q8jRP+L8mKKS/nqipKYRhI9PspXXUR1yIxEI9AIJz788MPcO+OP+Pbbb/O1ReCx0UYb5c8isKksKjd23333/Npll11yxUf07IiAZ1bu32233Zan/dSpU2N+7QAAADDHaszTb0z9iEqOZZddNj/wR7ARUzZiRZLo3RFhSFRnXHrppbmiYVqNOOOBPyoeolHoGmuskas/4hUNPqPnxXrrrZdDkZh6MivL0UblydFHH52ns0QAEX06oklo9B+JpqSzqlmzZnnFlr/+9a+56iKmtET/kvJiZZrYFn054tr/+c9/5gqSWO1lVhxxxBE5VImeH9EHJYKS0aNHp5tuuimvQFO5OgUAAABquhqzekuslhINTh944IHUpUuX9Kc//Sn3yig19IwQI4KBmL4R00JidZPKS7rGCi7R2DQqJaK6o9SwM6pDoqFnVFnsueeeeVWXWekBcuCBB+bAYPjw4Xmp3FjONlaUiYamsyNCjAgfololxn7MMcekCy64YKrpMDHe6CsS1x/Tee65556yqpaZiaqUCIIi1IleIDHeaMwaocmsHgMAAABqklpTKjezgJmIipZ4lUTT2AiNWve9JdWu98cbxgIAAMxNYwb+v5UzKYZ4Do1ZFxMmTMitIKbHX/Ez26KCJv7hKr0i8AAAAIDqRujBbDv55JNzmlZ6ffLJJ1U9JAAAAKi5jUypPurVq5dfAAAAUJ2p9AAAAAAKSegxE++8805eKaZ+/fppzTXXTNVBLL07ePDgsve1atVKd955Z5WOCQAAAKob01tmon///qlhw4bp3XffTYsuumiqjsaNG5eaNWs2S/uefvrpOSB59dVX5/m4AAAAoCoJPWbigw8+SD179kxt2rSZ7j6//fZbqlu3bqoqLVu2rLJzAwAAQHVVmOktv/zySzr66KPTEksskaeibLjhhumFF14o2/7YY4/laSAPP/xwWnvttVODBg3S+uuvnys4pif2f+mll9IZZ5yRf44qiTFjxuSfb7755rTJJpvkc11//fV5/6uuuip17Ngxf7bSSiulyy67rMLxRo4cmTp16pS3xxjuuOOOfKwZVV189dVXadttt02LLLJIateuXdm5Ko+z/PSWTz/9NO2xxx6pefPmuUolzvX888+na665Jg0YMCC99tpr+Tvxis+mTJmSr23ZZZfNDUpbtWqV7yUAAADUZIWp9DjhhBPSbbfdlq699tpclXH++een7t27p9GjR+eH/5JTTjklDRo0KC2++OLp0EMPTQcccEB6+umnpzttpFu3bqlHjx6pX79+eXrLN998k7eddNJJ+TilECPCiNNOOy0NHTo0f/bKK6+kgw46KIcO++23X/rxxx/TNttsk7bYYot03XXXpY8++ij16dNnptfVq1ev9Pnnn6dHH300V5NEGBFByPTEeSKMWXrppdOIESNyFcjLL7+cJk+enHbfffc0atSodN9996WHHnoo79+kSZN83y6++OJ00003pVVWWSV98cUXORgBAACAmqwQocekSZPSsGHDctXCVlttlT+78sor04MPPpj+9re/peOPP75s37PPPjuHAqXgIqau/Pzzzzm4qCwCgzp16uSwozSFpBR69O3bN+20004Ven9ECFL6LKoy3nrrrXTFFVfk0OOGG27IwUOMJ84V4UJUZBx22GHTva733nsv3XvvvblCpEuXLvmz+H5Uk0xPnOfrr7/OVS6lsKdDhw5l2+Na4prKT4kZO3Zsfh8BTwQrUfGxzjrrzMKdBwAAgOqrdlH6bkRfjQ022KDss3h4jwf3t99+u8K+q6++etnPSy21VP5zRpUT0xNTRsqHLjGG3r1751Ch9DrrrLPy5yHGEecuH66st956MzxHfCcCirXWWqvss5g207Rp0+l+J6bKRKVJ+eqWmdl1113Tf/7zn9S+fftcnRLTbv773//O8vcBAACgOipEpcfsKN9wNHpahKjAmF0xbaX8lJJSdcm6665bYb+FFloozU/R+2N2tW7dOvc2iSkvUR1z+OGHpwsuuCA9/vjjVdqgFQAAANKCXumx3HLLpYUXXrhCb46o/IgpHiuvvPI8P/+SSy6Zm39++OGHeSpJ+VdMcwkxJeX111/PU2lKnnvuuRkeN6o6ouIimqmWRDjx/fffT/c7UU0S1R7jx4+f5va4T7///vs0w5JomDpkyJDc9PXZZ59Nb7zxxixdPwAAAFRHhQg9ouoiemNE745o0hm9NGKaxk8//ZSnnMwPsSrKueeem0OD6MURgcHw4cPTRRddlLfvueeeubIkxhXju+eee9KFF144w2OuuOKKuYnqIYcckldfifDjwAMPnGE1R6zaEv05dthhhxwCRRATjUojxAht27bNTVQjGIn+JLHqTfRCiV4h0eQ09o9Gq3GOGS3TCwAAANVdIUKPMHDgwLTzzjunffbZJ3Xu3Dmv2nL//fenZs2azZfzRxgRS9ZG0LHaaqvlZqkRJpQqPaLHx913353DkOi5EavInHfeeTM9bhwvqkjieNEk9eCDD87L8k5PVHI88MADeZ+tt946jyXuTWmaTdyjCFK6du2aV7C58cYbc4+QmJoTPVGiUiSmucRYW7RoMRfvEAAAAMxftaZMmTJlPp+T/zNmzJgc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34hpIbG6SeUlXWMFl2hsGpUSUd1RatgZ1SHR0DOqLPbcc8+8qsus9AA58MADc2AwfPjwvFRuLGcbK8pEQ9PZESFGhA9RrRJjP+aYY9IFF1ww1XSYGG/0FYnrj+k899xzT1lVy8xEVUoEQRHqRC+QGG80Zo3QZFaPAQAAADVJrSmVm1nATERFS7xKomlshEat+96Satf74w1jAQAAimjMwP+3gid/TDyHxqyLCRMm5FYQ0+Ov+JltUUET/3CVXhF4AAAAQHUj9GC2nXzyyTlNK70++eSTqh4SAAAA1NxGplQf9erVyy8AAACozlR6AAAAAIUk9KgBatWqle68886qHgYAAADUKEKP+Uh4AQAAAPOP0COl9Ntvv1X1EAAAAIAFKfT45Zdf0tFHH52WWGKJVL9+/bThhhumF154oWz7Y489lqsnHn744bT22munBg0apPXXXz+9++670z3mmDFj8nduvvnmtMkmm+TjXn/99enbb79Ne+yxR1p66aXzcVZbbbV04403ln3vX//6V2ratGn6/fff8/tXX301H+ekk04q2+fAAw9Me++99zTP27Zt2/znjjvumL9Xeh+GDRuWlltuubTwwgunFVdcMf3jH/+Y4X3p379/WmqppdLrr7+e3z/11FNpo402SossskhePjbu2aRJkyqc+5xzzkkHHHBAatSoUVp22WXTX//617Ltv/76azryyCPzMeN+tGnTJi9LCwAAADVZtQ49TjjhhHTbbbela6+9Nr388supQ4cOqXv37mn8+PEV9jvllFPSoEGD0osvvpjq1KmTH+5nJsKKPn36pLfffjsf8+eff05rrbVW+ve//51GjRqVDj744LTPPvukkSNH5v0jVPjhhx/SK6+8kt8//vjjabHFFsvBS0l8tummm07zfKWwZvjw4WncuHFl7++44448juOOOy6f95BDDkn7779/evTRR6c6xpQpU9JRRx2V/v73v6cnn3wyrb766umDDz5IPXr0SDvvvHMOQSLMiRAkQozy4v5EMBTjP/zww9Nhhx1WFg4NGTIkjRgxIt1yyy35swiByocyAAAAUBPVmhJP0tVQVCo0a9YsXXPNNWnPPfcsm4YSD+N9+/ZNxx9/fA4cunbtmh566KG0+eab533uueee1LNnz/Sf//wnVy1Mq9KjXbt2afDgwTlsmJFtttkmrbTSSunCCy/M7yMUiWqQfv365YqNLl26pAEDBuQqkQkTJqRlllkmvffee2n55Zef5vGiwiNCjh122KHssw022CCtssoqFSovdtttt3z9EcCUvvfPf/4zfzdCiwcffDBXpJSqSxZaaKF0xRVXlH0/Qo+oYoljxD2IexahTamCJH7lLVu2zGM/9NBDc2XIm2++me9jnGt2TZw4MTVp0iS17ntLql2vwWx/HwAAYEEwZmDPqh5CYZSeQ+NZvHHjxjWv0iMqGCLkiFCgpG7dummdddbJ1RnlRcVDSUzRCF999dUMjx9VD+XFtJUzzzwzT2tp3rx5WnTRRdP999+fxo4dW7ZPBAkRtERoEJUWO+20U+rYsWMOGaLKo1WrVtMNPKYnrqX8NYZ4X/kajznmmPT888+nJ554oizwCK+99loOhmK8pVdUrkyePDl99NFH07xHEWxE6FG6R7169crTdWJqTQQgDzzwwGxdAwAAAFRH1Tb0mB0RhpSUKhXioX9GGjZsWOH9BRdckC655JJ04okn5qklEQJEeBD9Lkpi6koEHBE0xDmjCiQ+iyAkQo8IReaVLbbYIn322Wc5iCnvxx9/zFNiYrylV4zv/fffz31CpnWPSvepdI86d+6cA5IIfaJCJipNdtlll3l2LQAAALBAhx6lxp5PP/102WdR+RG9MFZeeeW5fr44z/bbb58bka6xxhqpffv2eapKeaW+HhdffHFZwFEKPeI1vX4e5YOHUiPUkqgUKX+NpbFUvsbtttsu3XDDDXk6y0033VT2eQQWb731Vu53UvkV929WRTnQ7rvvnq688srcFyR6qVTunQIAAAA1SZ1UTUUlRjTbjN4dMd0kVhw5//zz008//ZR69+49188X01JuvfXW9Mwzz+ReIhdddFH68ssvK4QP8XlME4lGn0OHDs2fbbzxxrkyIgKZmVV6RG+NWGkmpq/Uq1cvHy+uL77fqVOn1K1bt3T33Xen22+/PffXqCz6iERfjmiwGg1boxojKlP+9Kc/5calEYjEfYsQJPp+lMY4M3GtMS0oxlC7du3cPySmv8RqNQAAAFBTVdvQIwwcODBPwYiH/KiwiD4cMb0jwoK57dRTT00ffvhhntISS9bG6i3RcDSaopQXwUZMISlVdUQgE8FIBCTRE2NGYgWVY489NldTRF+OaKoa54hpNdEsNRqrRpPVWOFlelUjEXSU7kkEFNFXJKbWxAo2UYkS/UaiSiaqNmZVLGMbgVJMiYmmqNGgNRrCxvEBAACgpqq2q7dQc1i9BQAAYOas3jL31PjVWwAAAAD+CKEHAAAAUEhCDwAAAKCQhB4AAABAIQk9AAAAgEISegAAAACFJPQAAAAACknoAQAAABSS0AMAAAAopDpVPQCKY9SA7qlx48ZVPQwAAADIVHoAAAAAhST0AAAAAApJ6AEAAAAUktADAAAAKCShBwAAAFBIQg8AAACgkIQeAAAAQCEJPQAAAIBCEnoAAAAAhST0AAAAAApJ6AEAAAAUUp2qHgDFsWr/+1Pteg2qehgAACygxgzsWdVDAKoZlR4AAABAIQk9AAAAgEISegAAAACFJPQAAAAACknoAQAAABSS0AMAAAAoJKEHAAAAUEhCDwAAAKCQhB4AAABAIQk9AAAAgEISegAAAACFJPQAAAAACknoAQAAABSS0AMAAAAoJKEHAAAAUEhCDwAAAKCQhB4AAABAIQk9AAAAgEISegAAAACFJPSowR577LFUq1at9P3331f1UAAAAKDame+hxxdffJGOOuqo1L59+1SvXr3UunXrtO2226aHH354vo4jwoI777yz7P2gQYNSs2bN0s8//zzVvj/99FNq3LhxGjJkyFw/7x+x/vrrp3HjxqUmTZrMleMBAABAkczX0GPMmDFprbXWSo888ki64IIL0htvvJHuu+++1LVr13TEEUekqrTPPvukSZMmpdtvv32qbbfeemv69ddf0957752qi99++y0tvPDCqWXLljlImRfimgEAAKCmmq+hx+GHH54f0EeOHJl23nnntMIKK6RVVlklHXvssem5557L+4wdOzZtv/32adFFF83VFbvttlv68ssvy47Rq1evtMMOO1Q4bt++fdOmm25a9j5+Pvroo9MJJ5yQmjdvnoOB008/vWx727Zt85877rhjHk+8X2KJJXLFydVXXz3VuOOzOGcc65NPPsljatq0aX4fY40wp/L+cV1RybLUUkulI488crrnLRk2bFhabrnlcpCx4oorpn/84x8Vjhn7xz7bbbddatiwYTr77LOnmt4S1x3vK79K44v9DjzwwLT44ovne7vZZpul1157rewccY/WXHPNdNVVV6V27dql+vXrz9bvFwAAABbI0GP8+PG5qiMqOuKhvbIIESZPnpxDhNj38ccfTw8++GD6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//B55mZY07p16zRu3Li06qqrzpXjAQAAQNFUu0qPWbH44ounBg0azLXjbbfddqlFixbp2muvnWrbE088kUaPHp169+6dqotff/01hzQtW7ZMderUmSfn+P3339PkyZPnybEBAACgRoYe3377bdpjjz3S0ksvnYOJ1VZbLd14440V9rn11lvz54ssskgOG7p165YmTZpUYZ8LL7wwLbXUUnn7EUcckX777bfpVkxElclVV12Vdtxxx3zOqMwYMWJEhePF+/i8fv36qWvXrjngKFWn1K1bN+2zzz7pmmuumep6rr766rTuuuumVVZZJe974IEH5tClcePGabPNNkuvvfZahf3vvvvu1KVLl3yexRZbLI8pbLrppunjjz9OxxxzTD5vvMpXmsTx69Wrl69t0KBBFY4Zn5155plp3333zeeNKpfK01uiCqd03PKvqMIJv/zyS+rXr1/+vTRs2DBfU2lbiGtv2rRpvk8rr7xyHsvYsWNn6XcOAAAAC0To8fPPP6e11lor/fvf/06jRo3KD+gRKIwcOTJvjykZEYoccMAB6e23384P3jvttFOaMmVK2TEeffTR9MEHH+Q/I5yIB/JpBRLlDRgwIO22227p9ddfT1tvvXXaa6+90vjx4/O2jz76KO2yyy55ak6EFIccckg65ZRTKnw/Kjnef//9XNlR8uOPP+aAplTlseuuu6avvvoq3Xvvvemll15KnTt3TptvvnnZeeKaI+SI87/yyivp4YcfTuuss07edvvtt6dlllkmnXHGGfkexCvEcWLcf/7zn9Mbb7yRTj/99PSXv/xlquuNEGiNNdbIx43tlV1yySVlx41Xnz590hJLLJFWWmmlvP3II49Mzz77bLrpppvyPYpr6dGjR77mkp9++imdd955OUB688038/enJQKUiRMnVngBAABAdVNrSvm0YSaimuC6667LVQyVp0JE2PHdd9/laoHKttlmm/zwHQ/uL7/8cg5FolKhTZs20zxHBCEResQUjhChQO3atfMDe6nyoW/fvvmVL6JWrXTqqafmaogQVSOLLrpoDifiwf6kk07KgUSECiWx/9lnn11hzOutt15accUVywKHqPI46qij0hdffJHDkp49e+bQI6ogSjp06JBOOOGEHO6sv/76qX379vkeTUvlcYcIZ77++uv0wAMPlH0Wx4vxRvBQ+l6nTp3SHXfcUbZP3L927drlEGTNNdescJ4IWOK4Dz30UNpggw1yxUaMK/5s1apV2X5RYROhzDnnnJOvef/998+VIxGuzEgEMxEyVda67y2pdr25N+0IAACA+WfMwJ6ppoi/fG/SpEmaMGFCnhEx1yo9YmpIPBiXf0VlQPkAJMKHmL7SvHnzHD7cf//9ZVMl4oE6qiNie1QbXHnllTl4KC+mepQCjxDTXCJsmJHVV1+97OeYvhEXXfrOu+++m6eclFeqwCgvqk+isuOHH34oCz1ijI0aNcqhR1R+xHSbuKbSK6pIIqAJcS/i2mZHVLtEMFFevI8KjLiXJWuvvfYsHS9CkKisGTp0aNlxI+yJY62wwgoVxv7444+XjT0svPDCFe7j9Jx88sn5H6zS65NPPpmNKwYAAID5Y7a7YEagENUN5X366adlP19wwQV5qkX03IhgI/aPyoZovhkizHjwwQfTM888k6sbLr300jzV5Pnnn8+VCyF6bJQXlRwza6o5J9+pLKaYRM+NW265JW288cbp6aefTueee27eFoFHhC/l+2CUlCpFokfJvBL3cWaiIiWaskbfkfKNV2Pscd9jKk35MClE+FES4y/fa2R6otKlfLULAAAAVEdzfemPCAq23377tPfee+f3ETy89957uTlmSTxYRxVCvE477bQ8zSWmbsyNZWGnJaas3HPPPVMte1tZVHREZUdUeEQFRFRGbLTRRnlb9O+IUCFWS4npJtMSVRLRxyOmiUxLVFKUr94IHTt2zPesvHgf564cUMxITC+K+x7TiC666KIK22JqTJw3Kl9K1wMAAABFN9cbmcYKKaVKjpi6EU1Dv/zyy7LtUdERPSRefPHFPOUl+k9ET4t4+J9XYgzvvPNOOvHEE3MAE5Ucpb4dlSsbokIixn755Zfn6S7l+19Ez49ohhoVKtFTI/aLKpW4ltC/f/+8Uk38Gdce00qiMWhJhCXRKPWzzz5L33zzTf7suOOOy0FJTAmKsUXj1piaEiutzO41xjSTIUOG5PsZAU28osImApTo8RGrv8T9jik50Vg2qliidwgAAAAU0VwPPaJBaFRFdO/ePS/T2rJlyxwUlESvjXjwjxVO4mE89o8lWrfaaqs0r8S0mejVEQ/8UY0xbNiwstVbKk/T2HDDDXNlSDRFiZCgJMKRqBaJaS9RyRFjj+kwsQztkksumfeJ6/3nP/+Zl32N5qKxpG1p1ZoQK7dEWLLccsvlZW9D3KsIYaJJ66qrrporX2K/aOg6O6I/R6zaEhU1MQ2n9IpgJgwfPjxfT4QscX3xO4lql2WXXfYP3FkAAAAoyOotRRIrt0Q1hyacc69rrtVbAAAAaq4xBVy9Za739KiuLrvssryCS6y+Ej0zouHqkUceWdXDAgAAAOaRBSb0iCVgzzrrrDR+/Pg8pSOmecTSqwAAAEAxLTChx8UXX5xfAAAAwIJhrjcyBQAAAKgOhB4AAABAIQk9AAAAgEISegAAAACFJPQAAAAACknoAQAAABSS0AMAAAAoJKEHAAAAUEhCDwAAAKCQhB4AAABAIQk9AAAAgEISegAAAACFJPQAAAAACknoAQAAABSS0AMAAAAopDpVPQCKY9SA7qlx48ZVPQwAAADIVHoAAAAAhST0AAAAAApJ6AEAAAAUktADAAAAKCShBwAAAFBIQg8AAACgkIQeAAAAQCEJPQAAAIBCEnoAAAAAhST0AAAAAApJ6AEAAAAUktADAAAAKKQ6VT0AimPV/ven2vUaVPUwAACA/zNmYM+qHgJUKZUeAAAAQCEJPQAAAIBCEnoAAAAAhST0AAAAAApJ6AEAAAAUktADAAAAKCShBwAAAFBIQg8AAACgkIQeAAAAQCEJPQAAAIBCEnoAAAAAhST0AAAAAApJ6AEAAAAUktADAAAAKCShBwAAAFBIQg8AAACgkIQeAAAAQCEJPQAAAIBCqnahR69evdIOO+www33atm2bBg8enBZkY8aMSbVq1UqvvvpqVQ8FAAAAan7oMb1A4rHHHssP4N9//32aH1544YV08MEHz7Xj3XbbbWmhhRZKn3322TS3L7/88unYY4/9w+eZm2FN69at07hx49Kqq646V44HAAAARVPtKj1mxeKLL54aNGgw14633XbbpRYtWqRrr712qm1PPPFEGj16dOrdu3eqLn799dcc0rRs2TLVqVNnnpzj999/T5MnT54nxwYAAIAaGXp8++23aY899khLL710DiZWW221dOONN1bY59Zbb82fL7LIIjls6NatW5o0aVKFfS688MK01FJL5e1HHHFE+u2336ZbMRFVJldddVXacccd8zmjMmPEiBEVjhfv4/P69eunrl275oCjVJ1St27dtM8++6Rrrrlmquu5+uqr07rrrptWWWWVvO+BBx6YQ5fG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KCShBwAAAFBIQg8AAACgkIQeAAAAQCEJPQAAAIBCEnoAAAAAhST0AAAAAApJ6AEAAAAUUp2qHgDFMWpA99S4ceOqHgYAAABkKj0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKqU5VD4DiWLX//al2vQZVPQwAgPlizMCeVT0EAGZCpQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAh1cjQ4+mnn06rrbZaqlu3btphhx3SY489lmrVqpW+//776X7nmmuuSU2bNq3w2V//+tfUunXrVLt27TR48OBU08zKdQMAAMCCqlqFHvEAP6PX6aefnvc79thj05prrpk++uijHGasv/76ady4calJkyazfK6JEyemI488Mp144onps88+S5MmTUrNmjVLP//881T7/vTTT6lx48ZpyJAhc+Ua77zzzjQ3zMl1AwAAwIKiWoUe8QBfekXlRQQN5T/r169f3u+DDz5Im222WVpmmWVy9cbCCy+cWrZsmQOFWTV27Nj022+/pZ49e6allloqHXTQQTn4uP3226fa99Zbb02//vpr2nvvvVN1EWOfk+ueHXHNAAAAUFNVq9AjHuBLr6heiIf58p998803+bNvv/02HXDAAfnnqPSY1jSP+HzZZZdNDRo0SDvuuGP+TvltMT0mtG/fPn83qjm23XbbdPXVV081rvgsptE0b948ffLJJ2m33XbLYUu833777dOYMWOm2n+VVVZJ9erVy4FKVJSEtm3b5j9jPHHO0vswbNiwtNxyy+UgY8UVV0z/+Mc/Khwz9o99tttuu9SwYcN09tlnT3Xdm2666TQrZErji/0OPPDAtPjii+dAKYKj1157rewcUUkTFTRXXXVVateuXapfv/4f/I0CAABA1alWocfMRP+NqPiIB/aoBImfd99996n2e/7551Pv3r1z2PDqq6+mrl27prPOOqtse3znoYceyj+PHDkyHyeOHd955JFH0scff1y274cffpieeOKJvC2qK7p3754aNWqUnnzyydxbZNFFF009evQoq4qIYOKII45IBx98cHrjjTfSiBEjUocOHfK2F154If85fPjwfM7S+zvuuCP16dMnHXfccWnUqFHpkEMOSfvvv3969NFHK1xXhBIRmMRxI/SpLKpUylfG7LTTTjlAWXLJJfP2XXfdNX311Vfp3nvvTS+99FLq3Llz2nzzzdP48ePLjjF69Oh022235WPFvZuWX375JU8PKv8CAACA6qZOqkEWWmihsukcUQkSP0/LJZdckoOIE044Ib9fYYUV0jPPPJPuu+++/H6RRRZJLVq0yD9H1UPpOBFotGrVKocSpf4hURUSgUiEAzfccEOaPHlyroQoTSmJfaPqI6outtxyyxyuRHgRIUZJly5dys4VYv/yY7/wwgtTr1690uGHH17Ws+S5557Ln0dgU7LnnnvmMKR8IFNeVJ6UXHzxxTnAiQAorvepp57KAU+EHlGBUjpv9BeJ6TsR0oQIb/7+97+XjXVazj333DRgwIAZ/q4AAACgqtWoSo9Z9fbbb6d11123wmfrrbfeLIUq++23Xw46pkyZkgOOa6+9NgcNscJLTAWJSoio9IgKj3hF0BDNT6PPSAQKn3/+eQ5IZne8G2ywQYXP4n18Xt7aa689S8eLSo6TTjop3XzzzTnwCTH2H3/8MYc9pbHHK5rBxthL2rRpM8PAI5x88slpwoQJZa+Y8gMAAADVTY2q9JgfYtpIVDJElUSEHvFAX6quiNBgrbXWStdff/1U34ugIIKReSl6eczMW2+9lf785z+ngQMH5sqTkhh79BeJipTKyi/lOyvniEqRUrUIAAAAVFeFDD06duyYp3WUF9NFZkU0E91kk01yM9Ko9ujWrVuufgjRAyOqJ5ZYYoncV2Raojnpww8/XGFaSnl169ZNv//++1Tjjf4gUWVSEu9XXnnlNDui0Ws0Y915553TMcccU2FbjP2LL75IderUqdBAFQAAAIqqkNNbjj766Ny/I3pWvP/++2no0KFl/TxmRTQtjUae0WA0fi7Za6+90mKLLZZXbIlGpjE1JCon4nyffvpp3id6gQwaNCgNGTIkn/vll19Ol1566VShSAQQ3333Xf7s+OOPz1NqoglqfOeiiy7K5y8t0TurIuyI1WpiDHH80itClghvYopPrELzwAMP5BVdos/JKaeckl588cXZOg8AAADUBIUMPf70pz+lK6+8Mjc0XWONNfJD/qmnnjpb4UFM34gAIUKCkngfK7nEUrixMkpUaEQoEj09SpUfUa0RK8tcdtllednabbbZJgcZJRGIPPjgg7k5aqdOnfJncY4Ya4Q08Z0rrrgiN0iNJWhnR4wtVn+JypSYylJ6xRSdaLx6zz33pI033jhP14leHzENJlaqKa3uAgAAAEVSa0rM4YA/IJasjdV0Wve9JdWu16CqhwMAMF+MGdizqocAkBb059AJEyZMt/1EYSs9AAAAAIQeAAAAQCEJPQAAAIBCEnoAAAAAhST0AAAAAApJ6AEAAAAUktADAAAAKCShBwAAAFBIQg8AAACgkIQeAAAAQCEJPQAAAIBCEnoAAAAAhST0AAAAAApJ6AEAAAAUktADAAAAKCShBwAAAFBIQg8AAACgkIQeAAAAQCHVqeoBUByjBnRPjRs3ruphAAAAQKbSAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJDqVPUAqPmmTJmS/5w4cWJVDwUAAIAFwMT/e/4sPY9Oj9CDP+zbb7/Nf7Zu3bqqhwIAAMAC5IcffkhNmjSZ7nahB39Y8+bN859jx46d4T9sFCNNjXDrk08+SY0bN67q4TAP+V0vOPyuFxx+1wsWv+8Fh9/1gsPvuqKo8IjAo1WrVmlGhB78YbVr//+tYSLw8C/fgiF+z37XCwa/6wWH3/WCw+96weL3veDwu15w+F3/P7Pyl+4amQIAAACFJPQAAAAACknowR9Wr1691L9///wnxeZ3veDwu15w+F0vOPyuFyx+3wsOv+sFh9/1nKk1ZWbruwAAAADUQCo9AAAAgEISegAAAACFJPQAAAAACknoAQAAABSS0IM/5H//939T27ZtU/369dO6666bRo4cWdVDYh544okn0rbbbptatWqVatWqle68886qHhLzyLnnnpu6dOmSGjVqlJZYYom0ww47pHfffbeqh8U8MGzYsLT66qunxo0b59d6662X7r333qoeFvPBwIED8/+W9+3bt6qHwlx2+umn599t+ddKK61U1cNiHvnss8/S3nvvnVq0aJEWWWSRtNpqq6UXX3yxqofFPBDPW5X/3Y7XEUccUdVDqxGEHsyxm2++OR177LF52aSXX345rbHGGql79+7pq6++quqhMZdNmjQp/34j5KLYHn/88fx/oM8991x68MEH02+//Za23HLL/M8AxbLMMsvkh9+XXnop/0fyZpttlrbffvv05ptvVvXQmIdeeOGFdMUVV+TAi2JaZZVV0rhx48peTz31VFUPiXngu+++SxtssEGqW7duDqzfeuutNGjQoNSsWbOqHhrz6H+7y/97Hf+NFnbdddeqHlqNYMla5lhUdsTfCA8dOjS/nzx5cmrdunU66qij0kknnVTVw2MeiVT5jjvuyBUAFN/XX3+dKz4iDNl4442rejjMY82bN08XXHBB6t27d1UPhXngxx9/TJ07d06XXXZZOuuss9Kaa66ZBg8eXNXDYi5XekQ15quvvlrVQ2Eei//Wfvrpp9OTTz5Z1UOhCkSl3r/+9a/0/vvv5/82Z8ZUejBHfv311/y3g926dSv7rHbt2vn9s88+W6VjA+aeCRMmlD0MU1y///57uummm3JFT0xzoZiiiqtnz54V/r+b4omHoJiO2r59+7TXXnulsWPHVvWQmAdGjBiR1l577fw3/fGXE506dUpXXnllVQ+L+fQcdt1116UDDjhA4DGLhB7MkW+++Sb/R/KSSy5Z4fN4/8UXX1TZuIC5J6q34m8Sonx21VVXrerhMA+88cYbadFFF0316tVLhx56aK7iWnnllat6WMwDEWrFVNTo20Oxq3CvueaadN999+W+PR999FHaaKON0g8//FDVQ2Mu+/DDD/PvePnll0/3339/Ouyww9LRRx+drr322qoeGvNYVHN9//33qVevXlU9lBqjTlUPAIDq+7fCo0aNMh+8wFZcccVcBh8VPbfeemvab7/98lQmwUexfPLJJ6lPnz55Dng0Hqe4ttpqq7Kfo29LhCBt2rRJt9xyi2lrBfyLiaj0OOecc/L7qPSI/8++/PLL8/+WU1x/+9vf8r/rUdHFrFHpwRxZbLHF0kILLZS+/PLLCp/H+5YtW1bZuIC548gjj8xzRR999NHc8JJiWnjhhVOHDh3SWmutlSsAomHxJZdcUtXDYi6L6ajRZDz6edSpUye/ItwaMmRI/jkqNymmpk2bphVWWCGNHj26qofCXLbUUktNFVB37NjRdKaC+/jjj9NDDz2UDjzwwKoeSo0i9GCO/0M5/iP54YcfrpA4x3vzwaHmit7WEXjENIdHHnkktWvXrqqHxHwU/zv+yy+/VPUwmMs233zzPJUpqnpKr/gb4uj3ED/HX2JQ3Oa1H3zwQX5Aplhi6mnlJeXfe++9XNlDcQ0fPjz3cIn+TMw601uYY7FcbZTPxX84rbPOOrkDfDTB23///at6aMyD/2gq/7dEMUc4/kM5mlsuu+yyVTo25v6UlhtuuCHdddddqVGjRmU9epo0aZIWWWSRqh4ec9HJJ5+cy2Pj3+GY7x+/98ceeyzPDadY4t/lyn15GjZsmFq0aKFfT8H069cvbbvttvnB9/PPP0/9+/fPodYee+xR1UNjLjvmmGPS+uuvn6e37LbbbmnkyJHpr3/9a35R3L+YiNAjnr+iSo9Z524xx3bfffe8nOVpp52WH4xi6btonFW5uSk134svvpi6du1aIfAK8T+60TCN4oimaGHTTTet8Hn8n6yGWcUS0x323XffNG7cuBxqxfz/CDy22GKLqh4aMIc+/fTTHHB8++23afHFF08bbrhheu655/LPFEuXLl1yVWYE2GeccUauzIy/gIwKLoopprXE9KVYtYXZU2tK1DIDAAAAFIyeHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAAFJLQAwAAACgkoQcAAABQSEIPAID5YNNNN019+/ad6X4bb7xxuuGGG1J1ctJJJ6WjjjqqqocBALNN6AEAUE2MGDEiffnll+nPf/5z2Wdt27ZNtWrVSjfddNNU+6+yyip52zXXXDPV/vFaaKGFUqtWrVLv3r3Td999N9X3H3/88dS6deuZjqtfv37p2muvTR9++OEfuj4AmN+EHgAA1cSQIUPS/vvvn2rXrvifaBFMDB8+vMJnzz33XPriiy9Sw4YNpzrOGWeckcaNG5fGjh2brr/++vTEE0+ko48+eqr97rrrrrTtttvOdFyLLbZY6t69exo2bNgcXRcAVBWhBwBQKLfeemtabbXV0iKLLJJatGiRunXrliZNmpS39erVK+2www5pwIABafHFF0+NGzdOhx56aPr111/Lvj958uR07rnnpnbt2uVjrLHGGvmY5Y0aNSpttdVWadFFF01LLrlk2meffdI333xTtj3Ot+++++btSy21VBo0aNBMx/3111+nRx55ZJohxF577ZWrMj755JOyz66++ur8eZ06dabav1GjRqlly5Zp6aWXTl27dk377bdfevnll6dZWbLddtvN9L6FGNe0qk0AoDoTegAAhRHVDXvssUc64IAD0ttvv50ee+yxtNNOO6UpU6aU7fPwww+XbbvxxhvT7bffnkOQkgg8/v73v6fLL788vfnmm+mYY45Je++9dw4dwvfff58222yz1KlTp/Tiiy+m++67L09J2W233cqOcfzxx+f9o5LigQceyOeaVuhQ3lNPPZUaNGiQOnbsONW2CFai0iKmmISffvop3Xzzzfk6Z+azzz5Ld999d1p33XUrfB7X9tVXX+VrmZX7ts4666RPP/00jRkzZqbnBIDqYuq/GgAAqKHi4f2///1vfmBv06ZN/iyqF8pbeOGFc5VEBAzREyOmgkRIceaZZ6bffvstnXPOOemhhx5K6623Xt6/ffv2OZC44oor0iabbJKGDh2aA4/YrySOF1NQ3nvvvdxD429/+1u67rrr0uabb563R1ixzDLLzHDsH3/8cQ43Kk9tKYlA4rjjjkunnHJKrspYbrnl0pprrjnNfU888cR06qmnpt9//z39/PPPOfC46KKLKuwTgUwEKXE/ZuW+xXWVxhl9QwCgJlDpAQAURkxFiaAhHth33XXXdOWVV07VwDP2icCjJMKNH3/8MU8dGT16dK6i2GKLLfLUlNIrKj8++OCDvP9rr72WHn300QrbV1pppbwt9olXTJcpX1nRvHnztOKKK85w7P/5z39S/fr1p7u9Z8+eeZzRnyNClhlVeUSI8+qrr6bXX389V7aUvh8hSPnQozS1ZVbuW0x7CXF/AKCmUOkBABRGrFby4IMPpmeeeSZPK7n00ktzZcTzzz+fe3TMTIQK4d///nfuh1FevXr1yvaJ/hbnnXfeVN+P/h0RnMyJaBY6rRVWSqJ3R/QO6d+/f76eO+64Y4bH6tChQ/55+eWXT4MHD87hToQ10asjKjteeeWVHITM6n0bP358/jN6oQBATaHSAwAolFiqdYMNNsh9OuLBPqZvlA8IolIjqirKr4IS1RoxPWXllVfO4UasehKhQflXaWnXzp07534YMcWj8j6xkkpMO6lbt24ODEoizIipLzMSU2ZiNZYZBR9R3RG9QrbffvvUrFmzWb4nEWqE0nVHj4/1118/V6DM6n2L5q1xXTElCABqCpUeAEBhRNAQ0zm23HLLtMQSS+T3sSpK+eagMfWkd+/euedFNOWMyokjjzwy99KIVU/69euXm5fGKi4bbrhhmjBhQnr66afzSi+xCsoRRxyRp39E488TTjghBwdR3RErm1x11VU5QInjxxSTWAUlxhFVE9Pr1VE+9IgKjTjXNttsM8194jpilZjy03Om5YcffsgBSjQijWk7Mc6o0Iigo/KqLbN635588sm00UYblU1zAYCaQOgBABRGBBPR8yKmc0ycODE35YzlYmN52ZLoXRFTPjbeeOP0yy+/5PDi9NNPL9seDU0jIIhVXD788MPUtGnTXN3xP//zP2UNPSOYiGahERLEMeI8PXr0KAs2LrjggrJpMBGkRAPSCE9mVo2x//77p+uvv366oUeIIGVmTjvttPwKcS1dunTJ01biu7EMbQQccY9m575FqFP+PgFATVBrSvm1yAAACqxXr155ydk777wzVUdRnRHTR2J529IqKnNbLNEbVS5vvfXWLH/n3nvvzcFNNEaN3iIAUFPo6QEAUE20bNkyL3cbPUXmlZh+M60mrDMS1SHDhw8XeABQ46j0AAAWGNW90gMAmLuEHgAAAEAhmd4CAAAAFJLQAwAAACgkoQcAAABQSEIPAAAAoJCEHgAAAEAhCT0AAACAQhJ6AAAAAIUk9AAAAAAKSegBAAAApCL6/wCSRKww5svsggAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(12, 6))\n", + "\n", + "y_pos = np.arange(len(dict_count_vectorizers[\"vectorizer\"]))\n", + "ax.barh(y_pos, dict_count_vectorizers[\"speed\"], align=\"center\")\n", + "ax.set_yticks(y_pos)\n", + "ax.set_yticklabels(dict_count_vectorizers[\"vectorizer\"])\n", + "ax.invert_yaxis()\n", + "_ = ax.set_xlabel(\"speed (MB/s)\")" + ] + }, + { + "cell_type": "markdown", + "id": "ef9b977b-196d-4de7-8c29-0004a5c2e31e", + "metadata": {}, + "source": [ + "Задание успешно выполнено. Метод HashingVectorizer оказался самым быстрым. Он особенно быстрый на больших объемах данных и не требует хранения словаря, т.е. каждый токен сразу преобразуется в индекс по хеш-функции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f0e8c91-1de2-4839-aedb-618ffdc3f838", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.13.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}