diff --git a/README.md b/README.md index 46f2801e..92969fdc 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,25 @@ -# Шаблон сервиса рекомендаций - -## Подготовка - -### Python - -В данном шаблоне используется Python3.8, однако вы можете использовать более свежие версии, если хотите. -Но мы не гарантируем, что все будет работать. - -### Make - -[Make](https://www.gnu.org/software/make/) - это очень популярная утилита, -предназначенная для преобразования одних файлов в другие через определенную последовательность команд. -Однако ее можно использовать для исполнения произвольных последовательностей команд. -Команды и правила их исполнения прописываются в `Makefile`. - -Мы будем активно использовать `make` в данном проекте, поэтому рекомендуем познакомится с ней поближе. - -На MacOS и *nix системах `make` обычно идет в комплекте или ее можно легко установить. -Некоторые варианты, как можно поставить `make` на `Windows`, -описаны [здесь](https://stackoverflow.com/questions/32127524/how-to-install-and-use-make-in-windows). - -### Poetry - -[Poetry](https://python-poetry.org/) - это удобный инструмент для работы с зависимостями в Python. -Мы будем использовать его для подготовки окружения. - -Поэтому перед началом работы необходимо выполнить [шаги по установке](https://python-poetry.org/docs/#installation). - - -## Виртуальное окружение - -Мы будем работать в виртуальном окружении, которое создадим специально для данного проекта. -Если вы не знакомы с концепцией виртуальных окружений в Python, обязательно -[познакомьтесь](https://docs.python.org/3.8/tutorial/venv.html). -Мы рекомендуем использовать отдельное виртуальное окружение для каждого вашего проекта. - -### Инициализация окружения - -Выполните команду -``` -make setup -``` - -Будет создано новое виртуальное окружение в папке `.venv`. -В него будут установлены пакеты, перечисленные в файле `pyproject.toml`. - -Обратите внимание: если вы один раз выполнили `make setup`, при попытке повторного ее выполнения ничего не произойдет, -поскольку единственная ее зависимость - директория `.venv` - уже существует. -Если вам по какой-то причине нужно пересобрать окружение с нуля, -выполните сначала команду `make clean` - она удалит старое окружение. - -### Установка/удаление пакетов - -Для установки новых пакетов используйте команду `poetry add`, для удаления - `poetry remove`. -Мы не рекомендуем вручную редактировать секцию с зависимостями в `pyproject.toml`. - -## Линтеры, тесты и автоформатирование - -### Автоформатирование - -Командой `make format` можно запустить автоматическое форматирование вашего кода. - -Сейчас ее выполнение приведет лишь к запуску [isort](https://github.com/PyCQA/isort) - утилиты -для сортировки импортов в нужном порядке. -При желании вы также можете добавить другие инструменты, например [black](https://github.com/psf/black) или -[yapf](https://github.com/google/yapf), которые могут действительно отформатировать код. - - -### Статическая проверка кода - -Командой `make lint` вы запустите проверку линтерами - инструментами для статического анализа кода. -Они помогают выявить ошибки в коде еще до его запуска, а также обнаруживают несоответствия стандарту -[PEP8](https://peps.python.org/pep-0008). - -### Тесты - -Командой `make test` вы запустите тесты при помощи утилиты [pytest](https://pytest.org/). - - -## Запуск приложения - -### Способ 1: Python + Uvicorn - -``` -python main.py -``` - -Приложение запустится локально, в одном процессе. -Хост и порт по умолчанию: `127.0.0.1` и `8080`. -Их можно изменить через переменные окружения `HOST` и `PORT`. - -Управляет процессом легковесный [ASGI](https://asgi.readthedocs.io/en/latest/) server [uvicorn](https://www.uvicorn.org/). - -Обратите внимание: для запуска нужно использовать `python` из окружения проекта. - -### Способ 2: Uvicorn - -``` -uvicorn main:app -``` - -Очень похож на предыдущий, только запуск идет напрямую. -Хост и порт можно передать через аргументы командной строки. - -Обратите внимание: для запуска нужно использовать `uvicorn` из окружения проекта. - - -### Способ 3: Gunicorn - -``` -gunicorn main:app -c gunicorn.config.py -``` - -Способ похож на предыдущий, только вместо `uvicorn` используется -более функциональный сервер [gunicorn](https://gunicorn.org/) (`uvicorn` используется внутри него). -Параметры задаются через конфиг, хост и порт можно задать -через переменные окружения или аргументы командной строки. - -Сервис запускается в несколько параллельных процессов, по умолчанию их число -равно числу ядер процессора. - -Обратите внимание: для запуска нужно использовать `gunicorn` из окружения проекта. - -### Способ 4: Docker - -Делаем все то же самое, но внутри docker-контейнера. -Если вы не знакомы с [docker](https://www.docker.com/), обязательно познакомьтесь. - -Внутри контейнера можно использовать любой из способов, описанных выше. -В продакшене рекомендуется использовать `gunicorn`. - -Собрать и запустить образ можно командой - -``` -make run -``` - -## CI/CD - -Когда вы делаете пуш в гит (в любую ветку), выполняется процесс CI. -Что именно выполняется в этом процессе и как он триггерится (в данном случае по пушу), -описывается в специальных `.yaml` конфигах в папке `.github/workflows`. - -Сейчас там есть только один конфиг, который запускает процесс, в котором создается виртуальное окружение, -прогоняются линтеры и тесты. Если что-то пошло не так, процесс падает с ошибкой и в Github появляется красный крестик. -Вам нужно посмотреть логи, исправить ошибку и запушить изменения. +## Py-Spy +Запустил цикл в 1000 итераций, который отправлял запросы на каждую "ручку", скрины [тут](https://github.com/robertzaraev/RecoServiceTemplate/tree/HW_1/py-spy). +1. Health +2. Popular +3. kNN+bm25 + +Вывод: Так как популярные лейблы сразу написаны в классе, то неудивительно, что нагрузка с health примерно одинаковая. С моделью все немного интереснее, мы заранее сохраняли предикты для пользователей в csv файл, поэтому нагрузка идет на какие-то системные файлы, а также на файлы uvicorn'a. +## Sentry +Создал 3 ошибки, чтобы их трекал sentry +1. Юзер у которого id делится на 666 +2. Модель не найдена +3. Слишком большой userId + +Скрины [тут](https://github.com/robertzaraev/RecoServiceTemplate/tree/HW_1/sentry_pic). + +## MLFlow +У меня на компьютере не ставился rectools вообще, понижал версию питона - все равно ему что-то не нравится. Док-ва [тут](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/rectools1.png) и [тут](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/rectools2.png). Поэтому я использовал ресурсы [GoogleColab](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/knnfinal.ipynb) и [DataBricks](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/metrics/Вся%20информация%20о%20эксперименте.png) для того, чтобы трекать эксперименты. +1. Три метрики лучшей модели - [map@10](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/metrics/map%4010.png), [precision@10](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/metrics/precision.png), [recall@10](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/metrics/recall.png). +2. Продолжительная метрика: я использовал kNN, поэтому изменение метрик считал по мере увеличения соседей. +3. Техническая метрика: +- считал [вес](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/metrics/weight_mb.png) каждой модели, по мере увеличения кол-ва соседей +- считал [время обучения](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/metrics/learning_time.png) +- считал [время затрачиваемое на рекомендации](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/metrics/recos_time.png) +4. В конце ноутбука представлены примеры экспериментов + скачал лучшую модель, ноутбук [тут](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/knnfinal.ipynb). +5. Я развернул локально MLFlow, но локально затрекать эксперименты не смог из-за выше указанной причины. Прикладываю docker-compose и [скрин](https://github.com/robertzaraev/RecoServiceTemplate/blob/HW_1/mlflow/docker-localhost.png). diff --git a/main.py b/main.py index 8681c1d8..3a77ef9c 100644 --- a/main.py +++ b/main.py @@ -9,8 +9,6 @@ if __name__ == "__main__": - host = os.getenv("HOST", "127.0.0.1") - port = int(os.getenv("PORT", "8080")) - + port = int(os.getenv("PORT", "8000")) uvicorn.run(app, host=host, port=port) diff --git a/mlflow/docker-compose.yml b/mlflow/docker-compose.yml new file mode 100644 index 00000000..6cb215bd --- /dev/null +++ b/mlflow/docker-compose.yml @@ -0,0 +1,97 @@ +version: '3.7' + +services: + minio: + restart: always + image: minio/minio:RELEASE.2022-04-09T15-09-52Z + container_name: mlflow_s3 + ports: + - "9000:9000" + - "9001:9001" + command: server /data --console-address ':9001' --address ':9000' + environment: + - MINIO_ROOT_USER=${AWS_ACCESS_KEY_ID} + - MINIO_ROOT_PASSWORD=${AWS_SECRET_ACCESS_KEY} + volumes: + - minio_data:/data + + mc: + image: minio/mc:RELEASE.2022-04-07T21-43-27Z + depends_on: + - minio + container_name: mc + entrypoint: > + /bin/sh -c " + /tmp/wait-for-it.sh minio:9000 && + /usr/bin/mc alias set minio http://minio:9000 ${AWS_ACCESS_KEY_ID} ${AWS_SECRET_ACCESS_KEY} && + /usr/bin/mc mb minio/mlflow; + exit 0; + " + volumes: + - ./wait-for-it.sh:/tmp/wait-for-it.sh + + db: + restart: always + # image: mysql/mysql-server:8.0.28-1.2.7-server + image: postgres:15.1-alpine3.16 + container_name: mlflow_db + ports: + - "${DB_PORT}:${DB_PORT}" + environment: + # - MYSQL_DATABASE=${DB_NAME} + # - MYSQL_USER=${DB_USER} + # - MYSQL_PASSWORD=${DB_PASS} + # - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASS} + volumes: + # - dbdata:/var/lib/mysql + - dbdata:/var/lib/postgresql/data + + web: + restart: always + build: ./mlflow + image: mlflow_server + container_name: mlflow_server + depends_on: + - mc + - db + ports: + - "${MLFLOW_PORT}:${MLFLOW_PORT}" + environment: + - MLFLOW_S3_ENDPOINT_URL=http://minio:9000 + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} + command: > + mlflow server + --backend-store-uri ${DB_TYPE}://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME} + --default-artifact-root s3://mlflow/ + --host 0.0.0.0 + --port ${MLFLOW_PORT} + + nlp: + build: ./examples + image: mlflow_nlp_demo + depends_on: + - web + container_name: mlflow_client + environment: + - MLFLOW_TRACKING_URI=http://web:${MLFLOW_PORT} + - MLFLOW_S3_ENDPOINT_URL=http://minio:9000 + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} + command: > + /bin/sh -c " + /tmp/wait-for-it.sh web:${MLFLOW_PORT} -t 30 && + cd /work/examples && python main.py; + exit 0; + " + volumes: + - ./wait-for-it.sh:/tmp/wait-for-it.sh + - ./:/work + + +volumes: + dbdata: + minio_data: diff --git a/mlflow/docker-localhost.png b/mlflow/docker-localhost.png new file mode 100644 index 00000000..bbbfe39a Binary files /dev/null and b/mlflow/docker-localhost.png differ diff --git a/mlflow/knnfinal.ipynb b/mlflow/knnfinal.ipynb new file mode 100644 index 00000000..ec24784c --- /dev/null +++ b/mlflow/knnfinal.ipynb @@ -0,0 +1,1494 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "c2ZYQcUXr-Hj" + }, + "outputs": [], + "source": [ + "# import sys\n", + "# !{sys.executable} -m pip install rectools==0.2.0" + ] + }, + { + "cell_type": "code", + "source": [ + "# pip install mlflow\n", + "# pip install implicit\n", + "# pip install rectools" + ], + "metadata": { + "id": "gCjIrERi08pv" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!databricks configure --host https://community.cloud.databricks.com/" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lu9R_uP884R_", + "outputId": "a4fbc923-ce89-48de-e478-d0b0a24adc5c" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Username: robert.zaraev@mail.ru\n", + "Password: \n", + "Repeat for confirmation: \n" + ] + } + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zPMxceMYkV01", + "outputId": "6718bb8f-6ddb-429a-ddc7-00e04016295a" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Python 3.9.16\n" + ] + } + ], + "source": [ + "!python3 --version" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "xwoQaesPc-dn" + }, + "outputs": [], + "source": [ + "import os\n", + "import pickle\n", + "import random\n", + "import warnings\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "from implicit.nearest_neighbours import (\n", + " BM25Recommender,\n", + " CosineRecommender,\n", + " TFIDFRecommender,\n", + " ItemItemRecommender\n", + ")\n", + "# from implicit.nearest_neighbours import ItemItemRecommender\n", + "from rectools import Columns\n", + "from rectools.dataset import Dataset\n", + "from rectools.metrics import (\n", + " MAP,\n", + " MeanInvUserFreq,\n", + " Precision,\n", + " Recall,\n", + " Serendipity,\n", + " calc_metrics,\n", + ")\n", + "from rectools.model_selection import TimeRangeSplitter\n", + "from rectools.models import ImplicitItemKNNWrapperModel\n", + "from rectools.models.popular import PopularModel\n", + "from rectools.dataset import Interactions\n", + "\n", + "from typing import Dict, List, Optional, Set, Tuple\n", + "\n", + "import os\n", + "import time\n", + "warnings.filterwarnings(\"ignore\")" + ] + }, + { + "cell_type": "code", + "source": [ + "import mlflow\n", + "mlflow.set_tracking_uri(\"databricks\")\n", + "mlflow.set_experiment(\"/Users/robert.zaraev@mail.ru/ITMO\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dH8MMQNz9G4x", + "outputId": "069334ef-e990-42b4-a294-33224efa34de" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ] + }, + "metadata": {}, + "execution_count": 8 + } + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "wsIdcoM3r-Hr" + }, + "outputs": [], + "source": [ + "pd.set_option('display.max_columns', None)\n", + "pd.set_option('display.max_colwidth', 200)\n", + "\n", + "seed = 42\n", + "random.seed(seed)\n", + "np.random.seed(seed)\n", + "os.environ['PYTHONHASHSEED'] = str(seed)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "id": "q-MjS-lodBPz" + }, + "outputs": [], + "source": [ + "# download dataset by chunks\n", + "# !wget https://storage.yandexcloud.net/itmo-recsys-public-data/kion_train.zip -O ../data/data_original.zip\n", + "# !unzip ../data/data_original.zip -d ../data" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "eyAk_oJ9dDIo" + }, + "outputs": [], + "source": [ + "interactions = pd.read_csv('/content/interactions.csv')\n", + "users = pd.read_csv('/content/users.csv')\n", + "items = pd.read_csv('/content/items.csv')\n", + "\n", + "# rename columns, convert timestamp\n", + "interactions.rename(columns={'last_watch_dt': Columns.Datetime,\n", + " 'total_dur': Columns.Weight},\n", + " inplace=True)\n", + "\n", + "interactions['datetime'] = pd.to_datetime(interactions['datetime'])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h-0_YSEcgP0T" + }, + "source": [ + "## Train test split" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "u0b5O2ZogHda", + "outputId": "f56b7b2b-56b3-405d-85cd-b2b554be21e3" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Start date and last date of the test fold: (Timestamp('2021-08-08 00:00:00'), Timestamp('2021-08-22 00:00:00'))\n", + "Test fold borders: ['2021-08-08' '2021-08-15']\n", + "Real number of folds: 1\n" + ] + } + ], + "source": [ + "# train test split\n", + "# test = last 1 week\n", + "n_folds = 1\n", + "unit = \"W\"\n", + "n_units = 1\n", + "periods = n_folds + 1\n", + "\n", + "last_date = interactions[Columns.Datetime].max().normalize()\n", + "start_date = last_date - pd.Timedelta(n_folds * n_units + 1, unit=unit) # TimeDelta возвращает длительность промежутка между датами\n", + "print(f\"Start date and last date of the test fold: {start_date, last_date}\")\n", + "\n", + "date_range = pd.date_range(start=start_date, periods=periods, freq=unit, tz=last_date.tz)\n", + "print(f\"Test fold borders: {date_range.values.astype('datetime64[D]')}\")\n", + "\n", + "# generator of folds\n", + "cv = TimeRangeSplitter(\n", + " date_range=date_range,\n", + " filter_already_seen=True,\n", + " filter_cold_items=True,\n", + " filter_cold_users=True,\n", + ")\n", + "print(f\"Real number of folds: {cv.get_n_splits(Interactions(interactions))}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "Ja9ZQu32h2vk" + }, + "outputs": [], + "source": [ + "(train_ids, test_ids, fold_info) = cv.split(Interactions(interactions), collect_fold_stats=True).__next__()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "NN5AeKz1jHGT" + }, + "outputs": [], + "source": [ + "train = interactions.loc[train_ids].reset_index(drop=True)\n", + "test = interactions.loc[test_ids].reset_index(drop=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 206 + }, + "id": "cfaUmHKatbdC", + "outputId": "30ff31c9-fbf5-49f7-91d4-101764a0fd73" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " user_id item_id datetime weight watched_pct\n", + "0 176549 9506 2021-05-11 4250.0 72.0\n", + "1 699317 1659 2021-05-29 8317.0 100.0\n", + "2 656683 7107 2021-05-09 10.0 0.0\n", + "3 864613 7638 2021-07-05 14483.0 100.0\n", + "4 964868 9506 2021-04-30 6725.0 100.0" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_iddatetimeweightwatched_pct
017654995062021-05-114250.072.0
169931716592021-05-298317.0100.0
265668371072021-05-0910.00.0
386461376382021-07-0514483.0100.0
496486895062021-04-306725.0100.0
\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n", + " " + ] + }, + "metadata": {}, + "execution_count": 14 + } + ], + "source": [ + "train.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "id": "uOfF5DEqftHu" + }, + "outputs": [], + "source": [ + "# Create dataset\n", + "train_df = Dataset.construct(\n", + " train,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "id": "UnhXqplmbBfz" + }, + "outputs": [], + "source": [ + "metrics = {\n", + " \"mAP@10\": MAP(k=10),\n", + " \"prec@10\": Precision(k=10),\n", + " \"recall@10\": Recall(k=10),\n", + " \"novelty\": MeanInvUserFreq(k=10),\n", + " \"serendipity\": Serendipity(k=10),\n", + "}\n", + "\n", + "catalog = train['item_id'].unique()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "id": "7f61RWVIuIWd" + }, + "outputs": [], + "source": [ + "N = 10 # Количество рекомендаций" + ] + }, + { + "cell_type": "code", + "source": [ + "def return_weight(file_name:str):\n", + " os.stat(file_name)\n", + " return(os.stat(file_name).st_size / (1024 * 1024))" + ], + "metadata": { + "id": "qDZjgt30Aeiq" + }, + "execution_count": 28, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Трекинг (3 метрики качества, 3 тех метрики, артифакты, можно проследить качество моделей исходя из кол-ва соседей)" + ], + "metadata": { + "id": "DmLFiRWDlZZu" + } + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "id": "fTuw36uqd2m0", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "96619bf8-5a2e-4489-f43b-52022468487c" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "time_tfidf - 2.2707202434539795\n", + "0.9073276519775391\n", + "time_bm25 - 2.2333545684814453\n", + "0.9073276519775391\n", + "time_cossim - 2.2912590503692627\n", + "0.9073276519775391\n", + "time_tfidf - 2.181783437728882\n", + "1.738351821899414\n", + "time_bm25 - 2.1892642974853516\n", + "1.738351821899414\n", + "time_cossim - 2.2243709564208984\n", + "1.738351821899414\n", + "time_tfidf - 2.127289295196533\n", + "3.356466293334961\n", + "time_bm25 - 2.072434902191162\n", + "3.356466293334961\n", + "time_cossim - 2.4899699687957764\n", + "3.356466293334961\n" + ] + } + ], + "source": [ + "neighbours = [5, 10, 20] # Обучим модели на разном количестве соседей\n", + "\n", + "# Метрики будем складывать в список, чтобы потом поместить их в таблицу\n", + "tfidf = []\n", + "bm25 = []\n", + "cossim = []\n", + "\n", + "mlflow.start_run()\n", + "\n", + "for i in neighbours:\n", + "\n", + " # Fit model\n", + " start = time.time()\n", + " model_tfidf = ImplicitItemKNNWrapperModel(TFIDFRecommender(K=i))\n", + " model_tfidf.fit(train_df)\n", + " time_tfidf = time.time() - start \n", + " mlflow.log_metric('learning_time_tfidf', time_tfidf)\n", + " print('time_tfidf - ', time_tfidf)\n", + "\n", + " # Save model\n", + " path_tfidf = 'model_tfidf_' + str(i) + '.pickle'\n", + " with open(path_tfidf, 'wb') as pickle_out:\n", + " pickle.dump(model_tfidf, pickle_out)\n", + " mlflow.log_metric('weight_mb_tfidf', return_weight(path_tfidf))\n", + " print(return_weight(path_tfidf))\n", + "\n", + " # Make recommendations\n", + " start = time.time()\n", + " recos_tfidf = model_tfidf.recommend(\n", + " users=train[Columns.User].unique(),\n", + " dataset=train_df,\n", + " k=N,\n", + " filter_viewed=True,\n", + " )\n", + " recos_time_tfidf = time.time() - start\n", + " mlflow.log_metric(\"recos_time_tfidf\", recos_time_tfidf)\n", + "\n", + "\n", + " # Fit model\n", + " start = time.time()\n", + " model_bm25 = ImplicitItemKNNWrapperModel(BM25Recommender(K=i, K1=2)) # Изменение коэффициентов K1 и b особой роли не играет\n", + " model_bm25.fit(train_df)\n", + " time_bm25 = time.time() - start\n", + " mlflow.log_metric('learning_time_bm25', time_tfidf)\n", + " print('time_bm25 - ', time_bm25)\n", + "\n", + " # Save model\n", + " path_bm25 = 'model_bm25_' + str(i) + '.pickle'\n", + " with open(path_bm25, 'wb') as pickle_out:\n", + " pickle.dump(model_tfidf, pickle_out)\n", + " mlflow.log_metric('weight_mb_bm25', return_weight(path_bm25))\n", + " print(return_weight(path_bm25))\n", + "\n", + " # Make recommendations\n", + " start = time.time()\n", + " recos_bm25 = model_bm25.recommend(\n", + " users=train[Columns.User].unique(),\n", + " dataset=train_df,\n", + " k=N,\n", + " filter_viewed=True,\n", + " )\n", + " recos_time_bm25 = time.time() - start\n", + " mlflow.log_metric(\"recos_time_bm25\", recos_time_bm25)\n", + "\n", + " # Fit model\n", + " start = time.time()\n", + " model_cossim = ImplicitItemKNNWrapperModel(CosineRecommender(K=i)) \n", + " model_cossim.fit(train_df)\n", + " time_cossim = time.time() - start\n", + " mlflow.log_metric('learning_time_cossim', time_cossim)\n", + " print('time_cossim - ', time_cossim )\n", + "\n", + " # Save model\n", + " path_cossim = 'model_cossim_' + str(i) + '.pickle'\n", + " with open(path_cossim, 'wb') as pickle_out:\n", + " pickle.dump(model_tfidf, pickle_out)\n", + " mlflow.log_metric('weight_mb_cossim', return_weight(path_cossim))\n", + " print(return_weight(path_cossim))\n", + "\n", + " # Make recommendations\n", + " start = time.time()\n", + " recos_cossim = model_cossim.recommend(\n", + " users=train[Columns.User].unique(),\n", + " dataset=train_df,\n", + " k=N,\n", + " filter_viewed=True,\n", + " )\n", + " recos_time_bm25 = time.time() - start\n", + " mlflow.log_metric(\"recos_time_cossim\", recos_time_bm25)\n", + "\n", + " metric_values_tfidf = calc_metrics(\n", + " metrics,\n", + " reco=recos_tfidf,\n", + " interactions=test,\n", + " prev_interactions=train,\n", + " catalog=catalog\n", + " )\n", + "\n", + " metric_values_bm25 = calc_metrics(\n", + " metrics,\n", + " reco=recos_bm25,\n", + " interactions=test,\n", + " prev_interactions=train,\n", + " catalog=catalog\n", + " )\n", + "\n", + " metric_values_cossim = calc_metrics(\n", + " metrics,\n", + " reco=recos_cossim,\n", + " interactions=test,\n", + " prev_interactions=train,\n", + " catalog=catalog\n", + " )\n", + " mlflow.log_metric('prec@10_tfidf', metric_values_tfidf['prec@10'])\n", + " mlflow.log_metric('recall@10_tfidf', metric_values_tfidf['recall@10'])\n", + " mlflow.log_metric('mAP@10_tfidf', metric_values_tfidf['mAP@10'])\n", + "\n", + " mlflow.log_metric('prec@10_bm25', metric_values_bm25['prec@10'])\n", + " mlflow.log_metric('recall@10_bm25', metric_values_bm25['recall@10'])\n", + " mlflow.log_metric('mAP@10_bm25', metric_values_bm25['mAP@10'])\n", + "\n", + " mlflow.log_metric('prec@10_cossim', metric_values_cossim['prec@10'])\n", + " mlflow.log_metric('recall@10_cossim', metric_values_cossim['recall@10'])\n", + " mlflow.log_metric('mAP@10_cossim', metric_values_cossim['mAP@10'])\n", + "\n", + " mlflow.log_artifact(path_tfidf, 'kNN_tfidf_' + str(i) + 'neigh')\n", + " mlflow.log_artifact(path_bm25, 'kNN_bm25_' + str(i) + 'neigh')\n", + " mlflow.log_artifact(path_cossim, 'kNN_cossim_' + str(i) + 'neigh')\n", + "\n", + " tfidf.append(metric_values_tfidf)\n", + " bm25.append(metric_values_bm25)\n", + " cossim.append(metric_values_cossim)\n", + "\n", + "mlflow.end_run()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "id": "4DZXjMCUetIV" + }, + "outputs": [], + "source": [ + "dftfidf = pd.DataFrame(tfidf, index=['tfidf (k = 5)', 'tfidf (k = 10)', 'tfidf (k = 20)'])\n", + "dfbm25 = pd.DataFrame(bm25, index=['bm25 (k = 5)', 'bm25 (k = 10)', 'bm25 (k = 20)'])\n", + "dfcossim = pd.DataFrame(cossim, index=['cossim (k = 5)', 'cossim (k = 10)', 'cossim (k = 20)'])" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 332 + }, + "id": "aSQHRujEfYlW", + "outputId": "dedc3714-985c-4aad-eed7-fc35581a416b" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " prec@10 recall@10 mAP@10 novelty serendipity\n", + "tfidf (k = 5) 0.022427 0.118485 0.060637 8.533639 0.000022\n", + "tfidf (k = 10) 0.028080 0.150242 0.067100 8.030628 0.000019\n", + "tfidf (k = 20) 0.029198 0.155554 0.068653 7.796688 0.000018\n", + "bm25 (k = 5) 0.028065 0.148859 0.080727 3.995606 0.000013\n", + "bm25 (k = 10) 0.034657 0.187755 0.087131 4.208611 0.000007\n", + "bm25 (k = 20) 0.034678 0.189522 0.087413 4.190081 0.000005\n", + "cossim (k = 5) 0.014441 0.081689 0.043166 10.451231 0.000012\n", + "cossim (k = 10) 0.018092 0.101092 0.047973 10.083270 0.000011\n", + "cossim (k = 20) 0.020323 0.113794 0.050551 9.796017 0.000011" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prec@10recall@10mAP@10noveltyserendipity
tfidf (k = 5)0.0224270.1184850.0606378.5336390.000022
tfidf (k = 10)0.0280800.1502420.0671008.0306280.000019
tfidf (k = 20)0.0291980.1555540.0686537.7966880.000018
bm25 (k = 5)0.0280650.1488590.0807273.9956060.000013
bm25 (k = 10)0.0346570.1877550.0871314.2086110.000007
bm25 (k = 20)0.0346780.1895220.0874134.1900810.000005
cossim (k = 5)0.0144410.0816890.04316610.4512310.000012
cossim (k = 10)0.0180920.1010920.04797310.0832700.000011
cossim (k = 20)0.0203230.1137940.0505519.7960170.000011
\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n", + " " + ] + }, + "metadata": {}, + "execution_count": 33 + } + ], + "source": [ + "metricstable = pd.concat([dftfidf, dfbm25, dfcossim])\n", + "metricstable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ICg58UPIluoa" + }, + "source": [ + "По метрикам лучше всего себя показал BM25 (ожидаемо) и хуже всего обычное косинусное расстояние." + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Выгружаем эксперементы и лучшую модель" + ], + "metadata": { + "id": "imyXY_en3ABT" + } + }, + { + "cell_type": "code", + "source": [ + "mlflow.search_runs()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 260 + }, + "id": "vo1Zfze-neyG", + "outputId": "e35ba4b8-0fb1-45e2-e2ff-a87020ac6288" + }, + "execution_count": 45, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " run_id experiment_id status \\\n", + "0 79efe1ca1e6e4b84a7d3480e236ff4d3 3460239350097604 FINISHED \n", + "1 10836bbfda154627918fbcf9b1ccdef6 3460239350097604 FINISHED \n", + "2 504ac45d91b144438afbcc08c219a473 3460239350097604 FINISHED \n", + "\n", + " artifact_uri \\\n", + "0 dbfs:/databricks/mlflow-tracking/3460239350097604/79efe1ca1e6e4b84a7d3480e236ff4d3/artifacts \n", + "1 dbfs:/databricks/mlflow-tracking/3460239350097604/10836bbfda154627918fbcf9b1ccdef6/artifacts \n", + "2 dbfs:/databricks/mlflow-tracking/3460239350097604/504ac45d91b144438afbcc08c219a473/artifacts \n", + "\n", + " start_time end_time \\\n", + "0 2023-04-25 22:59:57.929000+00:00 2023-04-25 23:20:11.659000+00:00 \n", + "1 2023-04-25 16:37:48.780000+00:00 2023-04-25 16:56:22.199000+00:00 \n", + "2 2023-04-25 16:18:06.325000+00:00 2023-04-25 16:37:44.658000+00:00 \n", + "\n", + " metrics.recall@10_cossim metrics.prec@10_cossim metrics.recall@10_tfidf \\\n", + "0 0.113794 0.020323 0.155554 \n", + "1 0.097986 0.016172 0.138842 \n", + "2 NaN NaN NaN \n", + "\n", + " metrics.prec@10_tfidf metrics.mAP@10_tfidf metrics.prec@10_bm25 \\\n", + "0 0.029198 0.068653 0.034678 \n", + "1 0.024192 0.058766 0.030773 \n", + "2 NaN NaN NaN \n", + "\n", + " metrics.mAP@10_cossim metrics.mAP@10_bm25 metrics.recall@10_bm25 \\\n", + "0 0.050551 0.087413 0.189522 \n", + "1 0.041210 0.079598 NaN \n", + "2 NaN NaN NaN \n", + "\n", + " metrics.bm25_recall@10_bm25 params.weight_mb_bm25_10neigh \\\n", + "0 NaN 1.738351821899414 \n", + "1 0.178912 1.6741352081298828 \n", + "2 NaN 1.6741352081298828 \n", + "\n", + " params.weight_mb_tfidf_10neigh params.weight_mb_cossim_10neigh \\\n", + "0 1.738351821899414 1.738351821899414 \n", + "1 1.6741352081298828 1.6741352081298828 \n", + "2 1.6741352081298828 1.6741352081298828 \n", + "\n", + " params.recos_time_cossim_5neigh params.learning_time_cossim_20neigh \\\n", + "0 98.3430597782135 1.9751858711242676 \n", + "1 92.49501061439514 1.847031831741333 \n", + "2 91.27032613754272 None \n", + "\n", + " params.weight_mb_tfidf_5neigh params.recos_time_bm25_20neigh \\\n", + "0 0.9073276519775391 126.13504409790039 \n", + "1 0.8763904571533203 119.17588710784912 \n", + "2 0.8763904571533203 None \n", + "\n", + " params.learning_time_tfidf_20neigh params.learning_time_tfidf_5neigh \\\n", + "0 2.7743396759033203 1.911855936050415 \n", + "1 1.6906676292419434 1.6437160968780518 \n", + "2 None 2.2018229961395264 \n", + "\n", + " params.recos_time_bm25_5neigh params.recos_time_tfidf_5neigh \\\n", + "0 91.83148717880249 100.30975079536438 \n", + "1 79.68813848495483 93.90615487098694 \n", + "2 79.56686115264893 91.22894167900085 \n", + "\n", + " params.weight_mb_cossim_20neigh params.weight_mb_bm25_20neigh \\\n", + "0 3.356466293334961 3.356466293334961 \n", + "1 3.216157913208008 3.216157913208008 \n", + "2 None None \n", + "\n", + " params.learning_time_bm25_20neigh params.learning_time_bm25_5neigh \\\n", + "0 2.7743396759033203 1.911855936050415 \n", + "1 1.6906676292419434 1.6437160968780518 \n", + "2 None 2.2018229961395264 \n", + "\n", + " params.recos_time_tfidf_10neigh params.recos_time_tfidf_20neigh \\\n", + "0 123.38590145111084 130.7483367919922 \n", + "1 115.04857325553894 119.85497665405273 \n", + "2 116.28496193885803 None \n", + "\n", + " params.learning_time_cossim_5neigh params.recos_time_bm25_10neigh \\\n", + "0 1.8656017780303955 121.9310245513916 \n", + "1 1.7613751888275146 113.0884759426117 \n", + "2 2.11674427986145 112.90123152732849 \n", + "\n", + " params.learning_time_cossim_10neigh params.weight_mb_tfidf_20neigh \\\n", + "0 2.438255786895752 3.356466293334961 \n", + "1 1.9821107387542725 3.216157913208008 \n", + "2 1.6673228740692139 None \n", + "\n", + " params.recos_time_cossim_20neigh params.recos_time_cossim_10neigh \\\n", + "0 130.73950219154358 126.01592826843262 \n", + "1 120.48272609710693 116.10895538330078 \n", + "2 None 115.44647240638733 \n", + "\n", + " params.weight_mb_cossim_5neigh params.learning_time_bm25_10neigh \\\n", + "0 0.9073276519775391 2.425926685333252 \n", + "1 0.8763904571533203 2.039606809616089 \n", + "2 0.8763904571533203 3.2761800289154053 \n", + "\n", + " params.learning_time_tfidf_10neigh params.weight_mb_bm25_5neigh \\\n", + "0 2.425926685333252 0.9073276519775391 \n", + "1 2.039606809616089 0.8763904571533203 \n", + "2 3.2761800289154053 0.8763904571533203 \n", + "\n", + " params.recall@10_cossim params.prec@10_cossim params.bm25_recall@10_bm25 \\\n", + "0 None None None \n", + "1 None None None \n", + "2 0.06875471406528567 0.011362277686444476 0.13833516643202826 \n", + "\n", + " params.recall@10_tfidf params.prec@10_tfidf params.mAP@10_tfidf \\\n", + "0 None None None \n", + "1 None None None \n", + "2 0.10269398193307946 0.018004284362078415 0.051759436616506185 \n", + "\n", + " params.prec@10_bm25 params.mAP@10_cossim params.mAP@10_bm25 \\\n", + "0 None None None \n", + "1 None None None \n", + "2 0.02438599113236686 0.035407371155827955 0.07320059688073034 \n", + "\n", + " tags.mlflow.source.type tags.mlflow.user tags.mlflow.runName \\\n", + "0 LOCAL robert.zaraev@mail.ru thoughtful-bird-369 \n", + "1 LOCAL robert.zaraev@mail.ru unleashed-grub-357 \n", + "2 LOCAL robert.zaraev@mail.ru gifted-panda-293 \n", + "\n", + " tags.mlflow.source.name \n", + "0 /usr/local/lib/python3.9/dist-packages/ipykernel_launcher.py \n", + "1 /usr/local/lib/python3.9/dist-packages/ipykernel_launcher.py \n", + "2 /usr/local/lib/python3.9/dist-packages/ipykernel_launcher.py " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
run_idexperiment_idstatusartifact_uristart_timeend_timemetrics.recall@10_cossimmetrics.prec@10_cossimmetrics.recall@10_tfidfmetrics.prec@10_tfidfmetrics.mAP@10_tfidfmetrics.prec@10_bm25metrics.mAP@10_cossimmetrics.mAP@10_bm25metrics.recall@10_bm25metrics.bm25_recall@10_bm25params.weight_mb_bm25_10neighparams.weight_mb_tfidf_10neighparams.weight_mb_cossim_10neighparams.recos_time_cossim_5neighparams.learning_time_cossim_20neighparams.weight_mb_tfidf_5neighparams.recos_time_bm25_20neighparams.learning_time_tfidf_20neighparams.learning_time_tfidf_5neighparams.recos_time_bm25_5neighparams.recos_time_tfidf_5neighparams.weight_mb_cossim_20neighparams.weight_mb_bm25_20neighparams.learning_time_bm25_20neighparams.learning_time_bm25_5neighparams.recos_time_tfidf_10neighparams.recos_time_tfidf_20neighparams.learning_time_cossim_5neighparams.recos_time_bm25_10neighparams.learning_time_cossim_10neighparams.weight_mb_tfidf_20neighparams.recos_time_cossim_20neighparams.recos_time_cossim_10neighparams.weight_mb_cossim_5neighparams.learning_time_bm25_10neighparams.learning_time_tfidf_10neighparams.weight_mb_bm25_5neighparams.recall@10_cossimparams.prec@10_cossimparams.bm25_recall@10_bm25params.recall@10_tfidfparams.prec@10_tfidfparams.mAP@10_tfidfparams.prec@10_bm25params.mAP@10_cossimparams.mAP@10_bm25tags.mlflow.source.typetags.mlflow.usertags.mlflow.runNametags.mlflow.source.name
079efe1ca1e6e4b84a7d3480e236ff4d33460239350097604FINISHEDdbfs:/databricks/mlflow-tracking/3460239350097604/79efe1ca1e6e4b84a7d3480e236ff4d3/artifacts2023-04-25 22:59:57.929000+00:002023-04-25 23:20:11.659000+00:000.1137940.0203230.1555540.0291980.0686530.0346780.0505510.0874130.189522NaN1.7383518218994141.7383518218994141.73835182189941498.34305977821351.97518587112426760.9073276519775391126.135044097900392.77433967590332031.91185593605041591.83148717880249100.309750795364383.3564662933349613.3564662933349612.77433967590332031.911855936050415123.38590145111084130.74833679199221.8656017780303955121.93102455139162.4382557868957523.356466293334961130.73950219154358126.015928268432620.90732765197753912.4259266853332522.4259266853332520.9073276519775391NoneNoneNoneNoneNoneNoneNoneNoneNoneLOCALrobert.zaraev@mail.ruthoughtful-bird-369/usr/local/lib/python3.9/dist-packages/ipykernel_launcher.py
110836bbfda154627918fbcf9b1ccdef63460239350097604FINISHEDdbfs:/databricks/mlflow-tracking/3460239350097604/10836bbfda154627918fbcf9b1ccdef6/artifacts2023-04-25 16:37:48.780000+00:002023-04-25 16:56:22.199000+00:000.0979860.0161720.1388420.0241920.0587660.0307730.0412100.079598NaN0.1789121.67413520812988281.67413520812988281.674135208129882892.495010614395141.8470318317413330.8763904571533203119.175887107849121.69066762924194341.643716096878051879.6881384849548393.906154870986943.2161579132080083.2161579132080081.69066762924194341.6437160968780518115.04857325553894119.854976654052731.7613751888275146113.08847594261171.98211073875427253.216157913208008120.48272609710693116.108955383300780.87639045715332032.0396068096160892.0396068096160890.8763904571533203NoneNoneNoneNoneNoneNoneNoneNoneNoneLOCALrobert.zaraev@mail.ruunleashed-grub-357/usr/local/lib/python3.9/dist-packages/ipykernel_launcher.py
2504ac45d91b144438afbcc08c219a4733460239350097604FINISHEDdbfs:/databricks/mlflow-tracking/3460239350097604/504ac45d91b144438afbcc08c219a473/artifacts2023-04-25 16:18:06.325000+00:002023-04-25 16:37:44.658000+00:00NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN1.67413520812988281.67413520812988281.674135208129882891.27032613754272None0.8763904571533203NoneNone2.201822996139526479.5668611526489391.22894167900085NoneNoneNone2.2018229961395264116.28496193885803None2.11674427986145112.901231527328491.6673228740692139NoneNone115.446472406387330.87639045715332033.27618002891540533.27618002891540530.87639045715332030.068754714065285670.0113622776864444760.138335166432028260.102693981933079460.0180042843620784150.0517594366165061850.024385991132366860.0354073711558279550.07320059688073034LOCALrobert.zaraev@mail.rugifted-panda-293/usr/local/lib/python3.9/dist-packages/ipykernel_launcher.py
\n", + "
\n", + " \n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n", + " " + ] + }, + "metadata": {}, + "execution_count": 45 + } + ] + }, + { + "cell_type": "code", + "source": [ + "from mlflow.tracking import MlflowClient\n", + "MlflowClient().download_artifacts('79efe1ca1e6e4b84a7d3480e236ff4d3', 'kNN_bm25_10neigh/', '/content/' )" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 + }, + "id": "CO7oRb0bxVmz", + "outputId": "a97f604b-efa2-462f-ece7-8ba74fb27dcb" + }, + "execution_count": 71, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'/content/kNN_bm25_10neigh/'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 71 + } + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "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.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/mlflow/metrics/learning_time.png b/mlflow/metrics/learning_time.png new file mode 100644 index 00000000..221887d1 Binary files /dev/null and b/mlflow/metrics/learning_time.png differ diff --git a/mlflow/metrics/map@10.png b/mlflow/metrics/map@10.png new file mode 100644 index 00000000..68fb3a00 Binary files /dev/null and b/mlflow/metrics/map@10.png differ diff --git a/mlflow/metrics/precision.png b/mlflow/metrics/precision.png new file mode 100644 index 00000000..d54e59d3 Binary files /dev/null and b/mlflow/metrics/precision.png differ diff --git a/mlflow/metrics/recall.png b/mlflow/metrics/recall.png new file mode 100644 index 00000000..0d350a43 Binary files /dev/null and b/mlflow/metrics/recall.png differ diff --git a/mlflow/metrics/recos_time.png b/mlflow/metrics/recos_time.png new file mode 100644 index 00000000..0d3dc07f Binary files /dev/null and b/mlflow/metrics/recos_time.png differ diff --git a/mlflow/metrics/weight_mb.png b/mlflow/metrics/weight_mb.png new file mode 100644 index 00000000..599f3587 Binary files /dev/null and b/mlflow/metrics/weight_mb.png differ diff --git "a/mlflow/metrics/\320\222\321\201\321\217 \320\270\320\275\321\204\320\276\321\200\320\274\320\260\321\206\320\270\321\217 \320\276 \321\215\320\272\321\201\320\277\320\265\321\200\320\270\320\274\320\265\320\275\321\202\320\265.png" "b/mlflow/metrics/\320\222\321\201\321\217 \320\270\320\275\321\204\320\276\321\200\320\274\320\260\321\206\320\270\321\217 \320\276 \321\215\320\272\321\201\320\277\320\265\321\200\320\270\320\274\320\265\320\275\321\202\320\265.png" new file mode 100644 index 00000000..286fd22c Binary files /dev/null and "b/mlflow/metrics/\320\222\321\201\321\217 \320\270\320\275\321\204\320\276\321\200\320\274\320\260\321\206\320\270\321\217 \320\276 \321\215\320\272\321\201\320\277\320\265\321\200\320\270\320\274\320\265\320\275\321\202\320\265.png" differ diff --git a/mlflow/rectools1.png b/mlflow/rectools1.png new file mode 100644 index 00000000..f8caff71 Binary files /dev/null and b/mlflow/rectools1.png differ diff --git a/mlflow/rectools2.png b/mlflow/rectools2.png new file mode 100644 index 00000000..23c72f9a Binary files /dev/null and b/mlflow/rectools2.png differ diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..eb9d0f09 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,3 @@ +[mypy] +allow_untyped_calls = true +ignore_missing_imports = True diff --git a/notebooks/ALS_LightFM_MF.ipynb b/notebooks/ALS_LightFM_MF.ipynb new file mode 100644 index 00000000..fdb48263 --- /dev/null +++ b/notebooks/ALS_LightFM_MF.ipynb @@ -0,0 +1,4890 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "# Домашнее задание\n", + "\n", + "Домашнее задание состоит из нескольких блоков.\n", + "\n", + "\n", + "## Эксперименты в ipynb ноутбуках (15 баллов)\n", + "- Необходимо будет перебрать $N$ моделей $(N \\geq 2)$ матричной факторизации и перебрать у них $K$ гиперпараметров $(K \\geq 2)$ **(6 баллов)**\n", + " - Для перебора гиперпараметров можно использовать [`Optuna`](https://github.com/optuna/optuna), [`Hyperopt`](https://github.com/hyperopt/hyperopt)\n", + "- Воспользоваться методом приближенного поиска соседей для выдачи рекомендаций. **(3 балла)**\n", + " - Можно использовать любые удобные: [`Annoy`](https://github.com/spotify/annoy), [`nmslib`](https://github.com/nmslib/nmslib) и.т.д\n", + "- Добавить 3 \"аватаров\" (искусственных пользователей) и посмотреть рекомендации итоговой модели на них. Объяснить почему добавили именно таких пользователей. **(3 балла)**\n", + "- Придумать как можно обработать рекомендации для холодных пользователей. **(3 балла)**\n", + "\n", + "Примечание: за невоспроизводимый код в ноутбуках (например, нарушен порядок выполнения ячеек, вызываются переменные, которые нигде не были объявлены ранее и.т.п) будут штрафы на усмотрение проверяющего.\n", + "\n", + "\n", + "## Реализация итоговой модели в сервисе (10 баллов)\n", + "- Пробитие бейзлайна $MAP@10 \\geq 0.074921$ **(6 баллов)**\n", + "- Код сервиса соответствует критериям читаемости и воспроизводимости **(4 балла)**" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "cell_type": "code", + "execution_count": 246, + "id": "e42a5585", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import time\n", + "import random\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import optuna\n", + "import pandas as pd\n", + "\n", + "import nmslib\n", + "from implicit.als import AlternatingLeastSquares\n", + "from lightfm import LightFM\n", + "from rectools import Columns\n", + "from rectools.dataset import Dataset\n", + "from rectools.metrics import MAP, Precision, Recall, calc_metrics\n", + "from rectools.models import ImplicitALSWrapperModel, LightFMWrapperModel" + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "id": "8cd36e1a", + "metadata": {}, + "outputs": [], + "source": [ + "os.environ['OPENBLAS_NUM_THREADS'] = \"1\"" + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "id": "4cb722c3", + "metadata": {}, + "outputs": [], + "source": [ + "DATA_PATH = Path(\"../data/kion_train/\")" + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "id": "8195e56b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 2.61 s, sys: 924 ms, total: 3.53 s\n", + "Wall time: 5.74 s\n" + ] + } + ], + "source": [ + "%%time\n", + "users = pd.read_csv(DATA_PATH / 'users.csv')\n", + "items = pd.read_csv(DATA_PATH / 'items.csv')\n", + "interactions = pd.read_csv(DATA_PATH / 'interactions.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "id": "01c2bdef", + "metadata": {}, + "outputs": [], + "source": [ + "Columns.Datetime = 'last_watch_dt'" + ] + }, + { + "cell_type": "markdown", + "id": "00b5584e", + "metadata": {}, + "source": [ + "# Preprocess Interactions" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "id": "0a3b4b12", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pct
017654995062021-05-11425072.0
169931716592021-05-298317100.0
265668371072021-05-09100.0
386461376382021-07-0514483100.0
496486895062021-04-306725100.0
\n", + "
" + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct\n", + "0 176549 9506 2021-05-11 4250 72.0\n", + "1 699317 1659 2021-05-29 8317 100.0\n", + "2 656683 7107 2021-05-09 10 0.0\n", + "3 864613 7638 2021-07-05 14483 100.0\n", + "4 964868 9506 2021-04-30 6725 100.0" + ] + }, + "execution_count": 188, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "id": "260b1c48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "user_id int64\n", + "item_id int64\n", + "last_watch_dt object\n", + "total_dur int64\n", + "watched_pct float64\n", + "dtype: object" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions.dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "id": "fe2dbc06", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "user_id 0\n", + "item_id 0\n", + "last_watch_dt 0\n", + "total_dur 0\n", + "watched_pct 828\n", + "dtype: int64" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "interactions.isna().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "id": "71f61e81", + "metadata": {}, + "outputs": [], + "source": [ + "interactions[Columns.Datetime] = \\\n", + " pd.to_datetime(interactions[Columns.Datetime], format='%Y-%m-%d')" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "id": "39646cb5", + "metadata": {}, + "outputs": [], + "source": [ + "interactions.dropna(inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "id": "f98ae95d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "interactions['watched_pct'].hist(bins=20);" + ] + }, + { + "cell_type": "markdown", + "id": "1a0ad9f8", + "metadata": {}, + "source": [ + "Делю график просмотра на 5 категорий, где:\n", + "от 0% до 20% = 1,\n", + "от 21% до 40% = 2,\n", + "... ,\n", + "от 80% до 100% = 5" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "id": "1424e744", + "metadata": {}, + "outputs": [], + "source": [ + "interactions[Columns.Weight] = pd.cut(\n", + " x=interactions['watched_pct'],\n", + " bins=5,\n", + " labels=[1, 2, 3, 4, 5]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "id": "052d2fb0", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "interactions[Columns.Datetime].hist(bins=20);" + ] + }, + { + "cell_type": "markdown", + "id": "5adb80ee", + "metadata": {}, + "source": [ + "Вообще, видно, что кол-во пользователей растет" + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "id": "0fbd2bbc", + "metadata": {}, + "outputs": [], + "source": [ + "cold_users = \\\n", + " interactions[Columns.User].value_counts()[interactions[Columns.User].value_counts() == 1].index" + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "id": "150e1593", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "interactions[interactions[Columns.User].isin(cold_users)]['watched_pct'\n", + " ].hist(bins=20);" + ] + }, + { + "cell_type": "markdown", + "id": "b5fa83eb", + "metadata": {}, + "source": [ + "Для холодных пользователей, я бы рекомендовал айтемы с самый высоким просмотром в среднем по холодным пользователям" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "id": "cca06e3f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pctweight
1079146681992021-07-277139.01
26181144104402021-08-082740840.02
37161176104402021-07-29220.01
382172318192021-04-118535100.05
607721681432021-05-0866011.01
.....................
5476222100790097282021-08-124166.01
547622588213847162021-05-2115942.01
5476238805174131252021-06-2710.01
5476246648596122252021-08-13760.01
5476248697262152972021-08-201830763.04
\n", + "

355162 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct weight\n", + "10 791466 8199 2021-07-27 713 9.0 1\n", + "26 181144 10440 2021-08-08 27408 40.0 2\n", + "37 161176 10440 2021-07-29 22 0.0 1\n", + "38 21723 1819 2021-04-11 8535 100.0 5\n", + "60 77216 8143 2021-05-08 660 11.0 1\n", + "... ... ... ... ... ... ...\n", + "5476222 1007900 9728 2021-08-12 416 6.0 1\n", + "5476225 882138 4716 2021-05-21 1594 2.0 1\n", + "5476238 805174 13125 2021-06-27 1 0.0 1\n", + "5476246 648596 12225 2021-08-13 76 0.0 1\n", + "5476248 697262 15297 2021-08-20 18307 63.0 4\n", + "\n", + "[355162 rows x 6 columns]" + ] + }, + "execution_count": 199, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cold_users = interactions[interactions[Columns.User].isin(cold_users)]\n", + "hot_users = interactions[~interactions[Columns.User].isin(cold_users)]\n", + "cold_users" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "id": "a691676f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pctweight
017654995062021-05-11425072.04
169931716592021-05-298317100.05
265668371072021-05-09100.01
386461376382021-07-0514483100.05
496486895062021-04-306725100.05
.....................
5476246648596122252021-08-13760.01
547624754686296732021-04-13230849.03
5476248697262152972021-08-201830763.04
5476249384202161972021-04-196203100.05
547625031970944362021-08-15392145.03
\n", + "

5475423 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct weight\n", + "0 176549 9506 2021-05-11 4250 72.0 4\n", + "1 699317 1659 2021-05-29 8317 100.0 5\n", + "2 656683 7107 2021-05-09 10 0.0 1\n", + "3 864613 7638 2021-07-05 14483 100.0 5\n", + "4 964868 9506 2021-04-30 6725 100.0 5\n", + "... ... ... ... ... ... ...\n", + "5476246 648596 12225 2021-08-13 76 0.0 1\n", + "5476247 546862 9673 2021-04-13 2308 49.0 3\n", + "5476248 697262 15297 2021-08-20 18307 63.0 4\n", + "5476249 384202 16197 2021-04-19 6203 100.0 5\n", + "5476250 319709 4436 2021-08-15 3921 45.0 3\n", + "\n", + "[5475423 rows x 6 columns]" + ] + }, + "execution_count": 200, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hot_users" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "id": "a64701c2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_id
1044045695
1529739309
265720474
972815140
474011455
......
19461
138861
140901
57301
131251
\n", + "

6440 rows × 1 columns

\n", + "
" + ], + "text/plain": [ + " item_id\n", + "10440 45695\n", + "15297 39309\n", + "2657 20474\n", + "9728 15140\n", + "4740 11455\n", + "... ...\n", + "1946 1\n", + "13886 1\n", + "14090 1\n", + "5730 1\n", + "13125 1\n", + "\n", + "[6440 rows x 1 columns]" + ] + }, + "execution_count": 201, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cold_users['item_id'].value_counts().to_frame()" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "id": "cef9cfa6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Int64Index([609, 4895, 3825, 5069, 14092, 12593, 7142, 2271, 13673, 11566], dtype='int64', name='item_id')" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cold_users[['item_id', 'total_dur']].groupby('item_id').mean().sort_values(by='total_dur', ascending=False).head(10).index" + ] + }, + { + "cell_type": "markdown", + "id": "a52f311b", + "metadata": {}, + "source": [ + "# Делим на train и test" + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "id": "bd68d3fe", + "metadata": {}, + "outputs": [], + "source": [ + "max_date = interactions[Columns.Datetime].max()" + ] + }, + { + "cell_type": "code", + "execution_count": 204, + "id": "3092fe4b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train: (4984443, 6)\n", + "test: (490980, 6)\n" + ] + } + ], + "source": [ + "train = interactions[interactions[Columns.Datetime] < max_date\n", + " - pd.Timedelta(days=7)].copy()\n", + "test = interactions[interactions[Columns.Datetime] >= max_date\n", + " - pd.Timedelta(days=7)].copy()\n", + "\n", + "print(f\"train: {train.shape}\")\n", + "print(f\"test: {test.shape}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 205, + "id": "ec286bdc", + "metadata": {}, + "outputs": [], + "source": [ + "train.drop(train.query(\"total_dur < 300\").index, inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "id": "52241fed", + "metadata": {}, + "outputs": [], + "source": [ + "drop_user = set(test[Columns.User]) - set(train[Columns.User])\n", + "test.drop(test[test[Columns.User].isin(drop_user)].index, inplace=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b5eec6da", + "metadata": {}, + "source": [ + "## User preprocess" + ] + }, + { + "cell_type": "code", + "execution_count": 207, + "id": "c57e2b2f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_idageincomesexkids_flg
0973171age_25_34income_60_90М1
1962099age_18_24income_20_40М0
21047345age_45_54income_40_60Ж0
3721985age_45_54income_20_40Ж0
4704055age_35_44income_60_90Ж0
..................
840192339025age_65_infincome_0_20Ж0
840193983617age_18_24income_20_40Ж1
840194251008NaNNaNNaN0
840195590706NaNNaNЖ0
840196166555age_65_infincome_20_40Ж0
\n", + "

840197 rows × 5 columns

\n", + "
" + ], + "text/plain": [ + " user_id age income sex kids_flg\n", + "0 973171 age_25_34 income_60_90 М 1\n", + "1 962099 age_18_24 income_20_40 М 0\n", + "2 1047345 age_45_54 income_40_60 Ж 0\n", + "3 721985 age_45_54 income_20_40 Ж 0\n", + "4 704055 age_35_44 income_60_90 Ж 0\n", + "... ... ... ... ... ...\n", + "840192 339025 age_65_inf income_0_20 Ж 0\n", + "840193 983617 age_18_24 income_20_40 Ж 1\n", + "840194 251008 NaN NaN NaN 0\n", + "840195 590706 NaN NaN Ж 0\n", + "840196 166555 age_65_inf income_20_40 Ж 0\n", + "\n", + "[840197 rows x 5 columns]" + ] + }, + "execution_count": 207, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "users" + ] + }, + { + "cell_type": "code", + "execution_count": 208, + "id": "24cc138a", + "metadata": {}, + "outputs": [], + "source": [ + "users = users.loc[users[Columns.User].isin(train[Columns.User])].copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 209, + "id": "6e5e2d92", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_idageincomesexkids_flg
0973171age_25_34income_60_90М1
1962099age_18_24income_20_40М0
3721985age_45_54income_20_40Ж0
4704055age_35_44income_60_90Ж0
51037719age_45_54income_60_90М0
..................
840184529394age_25_34income_40_60Ж0
84018680113age_25_34income_40_60Ж0
840188312839age_65_infincome_60_90Ж0
840189191349age_45_54income_40_60М1
840190393868age_25_34income_20_40М0
\n", + "

586653 rows × 5 columns

\n", + "
" + ], + "text/plain": [ + " user_id age income sex kids_flg\n", + "0 973171 age_25_34 income_60_90 М 1\n", + "1 962099 age_18_24 income_20_40 М 0\n", + "3 721985 age_45_54 income_20_40 Ж 0\n", + "4 704055 age_35_44 income_60_90 Ж 0\n", + "5 1037719 age_45_54 income_60_90 М 0\n", + "... ... ... ... .. ...\n", + "840184 529394 age_25_34 income_40_60 Ж 0\n", + "840186 80113 age_25_34 income_40_60 Ж 0\n", + "840188 312839 age_65_inf income_60_90 Ж 0\n", + "840189 191349 age_45_54 income_40_60 М 1\n", + "840190 393868 age_25_34 income_20_40 М 0\n", + "\n", + "[586653 rows x 5 columns]" + ] + }, + "execution_count": 209, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "users" + ] + }, + { + "cell_type": "markdown", + "id": "c7f9a6e7", + "metadata": {}, + "source": [ + "Заменяю Nan'ы на Unknown" + ] + }, + { + "cell_type": "code", + "execution_count": 210, + "id": "1286eb60", + "metadata": {}, + "outputs": [], + "source": [ + "users.fillna('Unknown', inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 211, + "id": "1ccccf0f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
0973171Мsex
1962099Мsex
3721985Жsex
4704055Жsex
51037719Мsex
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 973171 М sex\n", + "1 962099 М sex\n", + "3 721985 Ж sex\n", + "4 704055 Ж sex\n", + "5 1037719 М sex" + ] + }, + "execution_count": 211, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "user_features_frames = []\n", + "for feature in ['sex', 'age', 'income']:\n", + " feature_frame = users.reindex(columns=[Columns.User, feature])\n", + " feature_frame.columns = ['id', 'value']\n", + " feature_frame['feature'] = feature\n", + " user_features_frames.append(feature_frame)\n", + "user_features = pd.concat(user_features_frames)\n", + "user_features.head()" + ] + }, + { + "cell_type": "markdown", + "id": "b9220b0b", + "metadata": {}, + "source": [ + "## Item preprocess" + ] + }, + { + "cell_type": "code", + "execution_count": 213, + "id": "8bbe9ee4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
item_idcontent_typetitletitle_origrelease_yeargenrescountriesfor_kidsage_ratingstudiosdirectorsactorsdescriptionkeywords
010711filmПоговори с нейHable con ella2002.0драмы, зарубежные, детективы, мелодрамыИспанияNaN16.0NaNПедро АльмодоварАдольфо Фернандес, Ана Фернандес, Дарио Гранди...Мелодрама легендарного Педро Альмодовара «Пого...Поговори, ней, 2002, Испания, друзья, любовь, ...
12508filmГолые перцыSearch Party2014.0зарубежные, приключения, комедииСШАNaN16.0NaNСкот АрмстронгАдам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ...Уморительная современная комедия на популярную...Голые, перцы, 2014, США, друзья, свадьбы, прео...
210716filmТактическая силаTactical Force2011.0криминал, зарубежные, триллеры, боевики, комедииКанадаNaN16.0NaNАдам П. КалтрароАдриан Холмс, Даррен Шалави, Джерри Вассерман,...Профессиональный рестлер Стив Остин («Все или ...Тактическая, сила, 2011, Канада, бандиты, ганг...
37868film45 лет45 Years2015.0драмы, зарубежные, мелодрамыВеликобританияNaN16.0NaNЭндрю ХэйАлександра Риддлстон-Барретт, Джеральдин Джейм...Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей...45, лет, 2015, Великобритания, брак, жизнь, лю...
416268filmВсе решает мгновениеNaN1978.0драмы, спорт, советские, мелодрамыСССРNaN12.0ЛенфильмВиктор СадовскийАлександр Абдулов, Александр Демьяненко, Алекс...Расчетливая чаровница из советского кинохита «...Все, решает, мгновение, 1978, СССР, сильные, ж...
\n", + "
" + ], + "text/plain": [ + " item_id content_type title title_orig release_year \\\n", + "0 10711 film Поговори с ней Hable con ella 2002.0 \n", + "1 2508 film Голые перцы Search Party 2014.0 \n", + "2 10716 film Тактическая сила Tactical Force 2011.0 \n", + "3 7868 film 45 лет 45 Years 2015.0 \n", + "4 16268 film Все решает мгновение NaN 1978.0 \n", + "\n", + " genres countries for_kids \\\n", + "0 драмы, зарубежные, детективы, мелодрамы Испания NaN \n", + "1 зарубежные, приключения, комедии США NaN \n", + "2 криминал, зарубежные, триллеры, боевики, комедии Канада NaN \n", + "3 драмы, зарубежные, мелодрамы Великобритания NaN \n", + "4 драмы, спорт, советские, мелодрамы СССР NaN \n", + "\n", + " age_rating studios directors \\\n", + "0 16.0 NaN Педро Альмодовар \n", + "1 16.0 NaN Скот Армстронг \n", + "2 16.0 NaN Адам П. Калтраро \n", + "3 16.0 NaN Эндрю Хэй \n", + "4 12.0 Ленфильм Виктор Садовский \n", + "\n", + " actors \\\n", + "0 Адольфо Фернандес, Ана Фернандес, Дарио Гранди... \n", + "1 Адам Палли, Брайан Хаски, Дж.Б. Смув, Джейсон ... \n", + "2 Адриан Холмс, Даррен Шалави, Джерри Вассерман,... \n", + "3 Александра Риддлстон-Барретт, Джеральдин Джейм... \n", + "4 Александр Абдулов, Александр Демьяненко, Алекс... \n", + "\n", + " description \\\n", + "0 Мелодрама легендарного Педро Альмодовара «Пого... \n", + "1 Уморительная современная комедия на популярную... \n", + "2 Профессиональный рестлер Стив Остин («Все или ... \n", + "3 Шарлотта Рэмплинг, Том Кортни, Джеральдин Джей... \n", + "4 Расчетливая чаровница из советского кинохита «... \n", + "\n", + " keywords \n", + "0 Поговори, ней, 2002, Испания, друзья, любовь, ... \n", + "1 Голые, перцы, 2014, США, друзья, свадьбы, прео... \n", + "2 Тактическая, сила, 2011, Канада, бандиты, ганг... \n", + "3 45, лет, 2015, Великобритания, брак, жизнь, лю... \n", + "4 Все, решает, мгновение, 1978, СССР, сильные, ж... " + ] + }, + "execution_count": 213, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 214, + "id": "72fa86a0", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "item_id 0\n", + "content_type 0\n", + "title 0\n", + "title_orig 4745\n", + "release_year 98\n", + "genres 0\n", + "countries 37\n", + "for_kids 15397\n", + "age_rating 2\n", + "studios 14898\n", + "directors 1509\n", + "actors 2619\n", + "description 2\n", + "keywords 423\n", + "dtype: int64" + ] + }, + "execution_count": 214, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items.isna().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 215, + "id": "50f0611f", + "metadata": {}, + "outputs": [], + "source": [ + "items = items.loc[items[Columns.Item].isin(train[Columns.Item])].copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 216, + "id": "280cf47d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "item_id 0\n", + "content_type 0\n", + "title 0\n", + "title_orig 3775\n", + "release_year 31\n", + "genres 0\n", + "countries 14\n", + "for_kids 13415\n", + "age_rating 1\n", + "studios 13047\n", + "directors 939\n", + "actors 1858\n", + "description 0\n", + "keywords 388\n", + "dtype: int64" + ] + }, + "execution_count": 216, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items.isna().sum()" + ] + }, + { + "cell_type": "markdown", + "id": "eddd93b5", + "metadata": {}, + "source": [ + "## Genre" + ] + }, + { + "cell_type": "code", + "execution_count": 217, + "id": "bf6da75c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711драмыgenre
010711зарубежныеgenre
010711детективыgenre
010711мелодрамыgenre
12508зарубежныеgenre
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 драмы genre\n", + "0 10711 зарубежные genre\n", + "0 10711 детективы genre\n", + "0 10711 мелодрамы genre\n", + "1 2508 зарубежные genre" + ] + }, + "execution_count": 217, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Explode genres to flatten table\n", + "items['genre'] = items['genres'].str.lower().str.replace(', ', ',',\n", + " regex=False).str.split(',')\n", + "genre_feature = items[['item_id', 'genre']].explode('genre')\n", + "genre_feature.columns = ['id', 'value']\n", + "genre_feature['feature'] = 'genre'\n", + "genre_feature.head()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 218, + "id": "913b9c27", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711драмыgenre
010711зарубежныеgenre
010711детективыgenre
010711мелодрамыgenre
12508зарубежныеgenre
............
1596010632криминалgenre
159614538драмыgenre
159614538спортgenre
159614538криминалgenre
159623206комедииgenre
\n", + "

36128 rows × 3 columns

\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 драмы genre\n", + "0 10711 зарубежные genre\n", + "0 10711 детективы genre\n", + "0 10711 мелодрамы genre\n", + "1 2508 зарубежные genre\n", + "... ... ... ...\n", + "15960 10632 криминал genre\n", + "15961 4538 драмы genre\n", + "15961 4538 спорт genre\n", + "15961 4538 криминал genre\n", + "15962 3206 комедии genre\n", + "\n", + "[36128 rows x 3 columns]" + ] + }, + "execution_count": 218, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "genre_feature" + ] + }, + { + "cell_type": "markdown", + "id": "478bf1e3", + "metadata": {}, + "source": [ + "## Content" + ] + }, + { + "cell_type": "code", + "execution_count": 220, + "id": "65f8b5d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711filmcontent_type
12508filmcontent_type
210716filmcontent_type
37868filmcontent_type
416268filmcontent_type
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 film content_type\n", + "1 2508 film content_type\n", + "2 10716 film content_type\n", + "3 7868 film content_type\n", + "4 16268 film content_type" + ] + }, + "execution_count": 220, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "content_feature = items.reindex(columns=[Columns.Item, \"content_type\"])\n", + "content_feature.columns = [\"id\", \"value\"]\n", + "content_feature[\"feature\"] = \"content_type\"\n", + "content_feature.head()" + ] + }, + { + "cell_type": "markdown", + "id": "a62ebee4", + "metadata": {}, + "source": [ + "## Actors" + ] + }, + { + "cell_type": "code", + "execution_count": 221, + "id": "fdc43660", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711адольфо фернандесactors
010711ана фернандесactors
010711дарио грандинеттиactors
010711джеральдин чаплинactors
010711елена анайяactors
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 адольфо фернандес actors\n", + "0 10711 ана фернандес actors\n", + "0 10711 дарио грандинетти actors\n", + "0 10711 джеральдин чаплин actors\n", + "0 10711 елена анайя actors" + ] + }, + "execution_count": 221, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items['actors'] = items['actors'].str.lower().str.replace(', ', ',',\n", + " regex=False).str.split(',')\n", + "actors_feature = items[['item_id', 'actors']].explode('actors')\n", + "actors_feature.columns = ['id', 'value']\n", + "actors_feature['feature'] = 'actors'\n", + "actors_feature.head()" + ] + }, + { + "cell_type": "markdown", + "id": "73801647", + "metadata": {}, + "source": [ + "## Keywords" + ] + }, + { + "cell_type": "code", + "execution_count": 222, + "id": "594abe5c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711поговориkeywords
010711нейkeywords
0107112002keywords
010711испанияkeywords
010711друзьяkeywords
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 поговори keywords\n", + "0 10711 ней keywords\n", + "0 10711 2002 keywords\n", + "0 10711 испания keywords\n", + "0 10711 друзья keywords" + ] + }, + "execution_count": 222, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "items['keywords'] = items['keywords'].str.lower().str.replace(', ', ','\n", + " , regex=False).str.split(',')\n", + "keywords_feature = items[['item_id', 'keywords']].explode('keywords')\n", + "keywords_feature.columns = ['id', 'value']\n", + "keywords_feature['feature'] = 'keywords'\n", + "keywords_feature.head()" + ] + }, + { + "cell_type": "markdown", + "id": "d3eb0a37", + "metadata": {}, + "source": [ + "## Countries" + ] + }, + { + "cell_type": "code", + "execution_count": 223, + "id": "8eb8a621", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711Испанияcountries
12508СШАcountries
210716Канадаcountries
37868Великобританияcountries
416268СССРcountries
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 Испания countries\n", + "1 2508 США countries\n", + "2 10716 Канада countries\n", + "3 7868 Великобритания countries\n", + "4 16268 СССР countries" + ] + }, + "execution_count": 223, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "country_feature = items.reindex(columns=[Columns.Item, 'countries'])\n", + "country_feature.columns = ['id', 'value']\n", + "country_feature['feature'] = 'countries'\n", + "country_feature.head()" + ] + }, + { + "cell_type": "markdown", + "id": "5e477ee1", + "metadata": {}, + "source": [ + "## Age Rating" + ] + }, + { + "cell_type": "code", + "execution_count": 224, + "id": "0894154a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
01071116.0age_feature
1250816.0age_feature
21071616.0age_feature
3786816.0age_feature
41626812.0age_feature
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 16.0 age_feature\n", + "1 2508 16.0 age_feature\n", + "2 10716 16.0 age_feature\n", + "3 7868 16.0 age_feature\n", + "4 16268 12.0 age_feature" + ] + }, + "execution_count": 224, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "age_feature = items.reindex(columns=[Columns.Item, \"age_rating\"])\n", + "age_feature.columns = [\"id\", \"value\"]\n", + "age_feature[\"feature\"] = \"age_feature\"\n", + "age_feature.head()" + ] + }, + { + "cell_type": "markdown", + "id": "c09df1ec", + "metadata": {}, + "source": [ + "## Studios" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "ff937b4f", + "metadata": {}, + "outputs": [], + "source": [ + "items.fillna('Unknown', inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 225, + "id": "cee42708", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711NaNstudios
12508NaNstudios
210716NaNstudios
37868NaNstudios
416268Ленфильмstudios
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 NaN studios\n", + "1 2508 NaN studios\n", + "2 10716 NaN studios\n", + "3 7868 NaN studios\n", + "4 16268 Ленфильм studios" + ] + }, + "execution_count": 225, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "studios_feature = items.reindex(columns=[Columns.Item, \"studios\"])\n", + "studios_feature.columns = [\"id\", \"value\"]\n", + "studios_feature[\"feature\"] = \"studios\"\n", + "studios_feature.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 226, + "id": "7234f13b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idvaluefeature
010711драмыgenre
010711зарубежныеgenre
010711детективыgenre
010711мелодрамыgenre
12508зарубежныеgenre
\n", + "
" + ], + "text/plain": [ + " id value feature\n", + "0 10711 драмы genre\n", + "0 10711 зарубежные genre\n", + "0 10711 детективы genre\n", + "0 10711 мелодрамы genre\n", + "1 2508 зарубежные genre" + ] + }, + "execution_count": 226, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# studios_feature, age_feature, country_feature - фичи которые хотелось бы использовать,\n", + "# но длительность обучения \"OPTUNA\" увеличивается в разы\n", + "item_features = pd.concat((genre_feature, content_feature ))\n", + "item_features.head()" + ] + }, + { + "cell_type": "markdown", + "id": "09b8d756", + "metadata": {}, + "source": [ + "## Metrics" + ] + }, + { + "cell_type": "code", + "execution_count": 227, + "id": "b082e667", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Precision@1': Precision(k=1),\n", + " 'Precision@2': Precision(k=2),\n", + " 'Precision@3': Precision(k=3),\n", + " 'Precision@4': Precision(k=4),\n", + " 'Precision@5': Precision(k=5),\n", + " 'Precision@6': Precision(k=6),\n", + " 'Precision@7': Precision(k=7),\n", + " 'Precision@8': Precision(k=8),\n", + " 'Precision@9': Precision(k=9),\n", + " 'Precision@10': Precision(k=10),\n", + " 'Recall@1': Recall(k=1),\n", + " 'Recall@2': Recall(k=2),\n", + " 'Recall@3': Recall(k=3),\n", + " 'Recall@4': Recall(k=4),\n", + " 'Recall@5': Recall(k=5),\n", + " 'Recall@6': Recall(k=6),\n", + " 'Recall@7': Recall(k=7),\n", + " 'Recall@8': Recall(k=8),\n", + " 'Recall@9': Recall(k=9),\n", + " 'Recall@10': Recall(k=10),\n", + " 'MAP@1': MAP(k=1, divide_by_k=False),\n", + " 'MAP@2': MAP(k=2, divide_by_k=False),\n", + " 'MAP@3': MAP(k=3, divide_by_k=False),\n", + " 'MAP@4': MAP(k=4, divide_by_k=False),\n", + " 'MAP@5': MAP(k=5, divide_by_k=False),\n", + " 'MAP@6': MAP(k=6, divide_by_k=False),\n", + " 'MAP@7': MAP(k=7, divide_by_k=False),\n", + " 'MAP@8': MAP(k=8, divide_by_k=False),\n", + " 'MAP@9': MAP(k=9, divide_by_k=False),\n", + " 'MAP@10': MAP(k=10, divide_by_k=False)}" + ] + }, + "execution_count": 227, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "metrics_name = {\n", + " 'Precision': Precision,\n", + " 'Recall': Recall,\n", + " 'MAP': MAP,\n", + "}\n", + "\n", + "metrics = {}\n", + "for metric_name, metric in metrics_name.items():\n", + " for k in range(1, 11):\n", + " metrics[f'{metric_name}@{k}'] = metric(k=k)\n", + "metrics" + ] + }, + { + "cell_type": "markdown", + "id": "2c603783", + "metadata": {}, + "source": [ + "# Optuna\n", + "\n", + "Необходимо будет перебрать $N$ моделей $(N \\geq 2)$ матричной факторизации и перебрать у них $K$ гиперпараметров $(K \\geq 2)$ **(6 баллов)**\n", + "Для перебора гиперпараметров можно использовать [`Optuna`](https://github.com/optuna/optuna), [`Hyperopt`](https://github.com/hyperopt/hyperopt)" + ] + }, + { + "cell_type": "code", + "execution_count": 229, + "id": "fb193c2f", + "metadata": {}, + "outputs": [], + "source": [ + "NUM_THREADS = 8\n", + "RANDOM_STATE = 42\n", + "K = 5\n", + "NO_COMPONENTS = 32\n", + "N_EPOCHS = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 230, + "id": "3328645d", + "metadata": {}, + "outputs": [], + "source": [ + "USER_ALPHA = 0\n", + "ITEM_ALPHA = 0\n", + "K_RECOS = 10" + ] + }, + { + "cell_type": "code", + "execution_count": 231, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "dataset = Dataset.construct(interactions_df=train,\n", + " user_features_df=user_features,\n", + " cat_user_features=['sex', 'age', 'income'],\n", + " item_features_df=item_features,\n", + " cat_item_features=['content_type', 'genre'])\n", + "TEST_USERS = test[Columns.User].unique()" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "a17b8b66", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 18:32:01,241]\u001b[0m A new study created in memory with name: no-name-d2c12238-d18d-4c6f-bca4-840e7d3f29b0\u001b[0m\n", + "\u001b[32m[I 2022-12-12 18:41:40,178]\u001b[0m Trial 0 finished with value: 0.07156314077881751 and parameters: {'recomender': 'ALS', 'regularization': 0.0035671951579306937}. Best is trial 0 with value: 0.07156314077881751.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07156314077881751\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 18:51:48,671]\u001b[0m Trial 1 finished with value: 0.07241639094290958 and parameters: {'recomender': 'ALS', 'regularization': 0.006724126527816994}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07241639094290958\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 18:53:18,697]\u001b[0m Trial 2 finished with value: 1.4050149479540313e-07 and parameters: {'recomender': 'LightFM', 'learning_rate': 0.8190152272980945, 'k': 6, 'loss': 'bpr'}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 1.4050149479540313e-07\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 18:54:36,340]\u001b[0m Trial 3 finished with value: 0.005366068762176463 and parameters: {'recomender': 'LightFM', 'learning_rate': 0.18723291153626603, 'k': 7, 'loss': 'bpr'}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.005366068762176463\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 18:56:21,781]\u001b[0m Trial 4 finished with value: 0.0002205827579874263 and parameters: {'recomender': 'LightFM', 'learning_rate': 0.6560234930178588, 'k': 6, 'loss': 'logistic'}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.0002205827579874263\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:05:46,138]\u001b[0m Trial 5 finished with value: 0.07173988822226253 and parameters: {'recomender': 'ALS', 'regularization': 0.009044651486228098}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07173988822226253\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:07:05,748]\u001b[0m Trial 6 finished with value: 3.943062688880311e-06 and parameters: {'recomender': 'LightFM', 'learning_rate': 0.44751878244334975, 'k': 7, 'loss': 'warp'}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 3.943062688880311e-06\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:08:22,099]\u001b[0m Trial 7 finished with value: 0.06793721324055035 and parameters: {'recomender': 'LightFM', 'learning_rate': 0.10438302844592218, 'k': 3, 'loss': 'warp'}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.06793721324055035\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:17:32,898]\u001b[0m Trial 8 finished with value: 0.07184311920591484 and parameters: {'recomender': 'ALS', 'regularization': 0.0005806772800434514}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07184311920591484\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:18:59,268]\u001b[0m Trial 9 finished with value: 2.2409988419866796e-06 and parameters: {'recomender': 'LightFM', 'learning_rate': 0.805131840226543, 'k': 7, 'loss': 'bpr'}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 2.2409988419866796e-06\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:28:09,202]\u001b[0m Trial 10 finished with value: 0.07180954187357708 and parameters: {'recomender': 'ALS', 'regularization': 0.008190821633209066}. Best is trial 1 with value: 0.07241639094290958.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07180954187357708\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:37:15,147]\u001b[0m Trial 11 finished with value: 0.07244222251339615 and parameters: {'recomender': 'ALS', 'regularization': 0.00026970098377155163}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07244222251339615\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:46:24,260]\u001b[0m Trial 12 finished with value: 0.07202031081710311 and parameters: {'recomender': 'ALS', 'regularization': 0.0066363513358619185}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07202031081710311\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 19:55:34,176]\u001b[0m Trial 13 finished with value: 0.07199398893026046 and parameters: {'recomender': 'ALS', 'regularization': 0.004156107607150058}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07199398893026046\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 20:04:42,589]\u001b[0m Trial 14 finished with value: 0.07167963259586223 and parameters: {'recomender': 'ALS', 'regularization': 0.0006228566403390988}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07167963259586223\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 20:13:49,348]\u001b[0m Trial 15 finished with value: 0.07202106855494453 and parameters: {'recomender': 'ALS', 'regularization': 0.00598705937836684}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07202106855494453\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 20:22:57,770]\u001b[0m Trial 16 finished with value: 0.07193963920172777 and parameters: {'recomender': 'ALS', 'regularization': 0.0028834604694796696}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07193963920172777\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 20:32:04,614]\u001b[0m Trial 17 finished with value: 0.07186249836889418 and parameters: {'recomender': 'ALS', 'regularization': 0.007249626991281137}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07186249836889418\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 20:41:24,258]\u001b[0m Trial 18 finished with value: 0.07164870366757094 and parameters: {'recomender': 'ALS', 'regularization': 0.005115162467576984}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07164870366757094\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 20:50:49,059]\u001b[0m Trial 19 finished with value: 0.07179004415938793 and parameters: {'recomender': 'ALS', 'regularization': 0.0018963762834092678}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07179004415938793\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 21:42:16,853]\u001b[0m Trial 20 finished with value: 0.07167129296212232 and parameters: {'recomender': 'ALS', 'regularization': 0.009804091535656343}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07167129296212232\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 21:52:20,413]\u001b[0m Trial 21 finished with value: 0.07191839766465505 and parameters: {'recomender': 'ALS', 'regularization': 0.005764203522687446}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07191839766465505\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 22:02:12,773]\u001b[0m Trial 22 finished with value: 0.07159025974450188 and parameters: {'recomender': 'ALS', 'regularization': 0.006209360331833826}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07159025974450188\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 22:12:55,936]\u001b[0m Trial 23 finished with value: 0.07167021208897958 and parameters: {'recomender': 'ALS', 'regularization': 0.007825089420401029}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07167021208897958\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 22:23:04,762]\u001b[0m Trial 24 finished with value: 0.07191850988173716 and parameters: {'recomender': 'ALS', 'regularization': 0.004979324924695012}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07191850988173716\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 22:34:38,654]\u001b[0m Trial 25 finished with value: 0.07178449755342503 and parameters: {'recomender': 'ALS', 'regularization': 0.002381286135370409}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07178449755342503\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 22:45:43,850]\u001b[0m Trial 26 finished with value: 0.07177449403191542 and parameters: {'recomender': 'ALS', 'regularization': 0.004677821831829}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07177449403191542\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 22:56:45,636]\u001b[0m Trial 27 finished with value: 0.07214439856613958 and parameters: {'recomender': 'ALS', 'regularization': 0.0069504676367203744}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07214439856613958\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 23:08:13,361]\u001b[0m Trial 28 finished with value: 0.07237690498752612 and parameters: {'recomender': 'ALS', 'regularization': 0.0074561399301387955}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07237690498752612\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m[I 2022-12-12 23:18:05,878]\u001b[0m Trial 29 finished with value: 0.07173589378156864 and parameters: {'recomender': 'ALS', 'regularization': 0.00848249465326267}. Best is trial 11 with value: 0.07244222251339615.\u001b[0m\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MAP@10 0.07173589378156864\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[33m[W 2022-12-12 23:27:26,180]\u001b[0m Trial 30 failed because of the following error: KeyboardInterrupt()\u001b[0m\n", + "Traceback (most recent call last):\n", + " File \"/home/rezarayev/anaconda3/lib/python3.8/site-packages/optuna/study/_optimize.py\", line 196, in _run_trial\n", + " value_or_values = func(trial)\n", + " File \"/tmp/ipykernel_5286/352742220.py\", line 40, in objective\n", + " recos = model.recommend(\n", + " File \"/home/rezarayev/anaconda3/lib/python3.8/site-packages/rectools/models/base.py\", line 132, in recommend\n", + " reco_user_ids, reco_item_ids, reco_scores = self._recommend_u2i(\n", + " File \"/home/rezarayev/anaconda3/lib/python3.8/site-packages/rectools/models/vector.py\", line 141, in _recommend_u2i\n", + " scores = scores_calculator.calc(target_id)\n", + " File \"/home/rezarayev/anaconda3/lib/python3.8/site-packages/rectools/models/vector.py\", line 98, in calc\n", + " scores = self.objects_factors @ subject_factors\n", + "KeyboardInterrupt\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/tmp/ipykernel_5286/352742220.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 48\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0mstudy\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0moptuna\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_study\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdirection\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'maximize'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 50\u001b[0;31m \u001b[0mstudy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moptimize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobjective\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_trials\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/optuna/study/study.py\u001b[0m in \u001b[0;36moptimize\u001b[0;34m(self, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)\u001b[0m\n\u001b[1;32m 417\u001b[0m \"\"\"\n\u001b[1;32m 418\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 419\u001b[0;31m _optimize(\n\u001b[0m\u001b[1;32m 420\u001b[0m \u001b[0mstudy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 421\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/optuna/study/_optimize.py\u001b[0m in \u001b[0;36m_optimize\u001b[0;34m(study, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)\u001b[0m\n\u001b[1;32m 64\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 65\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn_jobs\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 66\u001b[0;31m _optimize_sequential(\n\u001b[0m\u001b[1;32m 67\u001b[0m \u001b[0mstudy\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/optuna/study/_optimize.py\u001b[0m in \u001b[0;36m_optimize_sequential\u001b[0;34m(study, func, n_trials, timeout, catch, callbacks, gc_after_trial, reseed_sampler_rng, time_start, progress_bar)\u001b[0m\n\u001b[1;32m 158\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 159\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 160\u001b[0;31m \u001b[0mfrozen_trial\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_run_trial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstudy\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 161\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 162\u001b[0m \u001b[0;31m# The following line mitigates memory problems that can be occurred in some\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/optuna/study/_optimize.py\u001b[0m in \u001b[0;36m_run_trial\u001b[0;34m(study, func, catch)\u001b[0m\n\u001b[1;32m 232\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfunc_err\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcatch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 233\u001b[0m ):\n\u001b[0;32m--> 234\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mfunc_err\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 235\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mfrozen_trial\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 236\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/optuna/study/_optimize.py\u001b[0m in \u001b[0;36m_run_trial\u001b[0;34m(study, func, catch)\u001b[0m\n\u001b[1;32m 194\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mget_heartbeat_thread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrial\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_trial_id\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstudy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_storage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 195\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 196\u001b[0;31m \u001b[0mvalue_or_values\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtrial\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 197\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mexceptions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTrialPruned\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 198\u001b[0m \u001b[0;31m# TODO(mamu): Handle multi-objective cases.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/tmp/ipykernel_5286/352742220.py\u001b[0m in \u001b[0;36mobjective\u001b[0;34m(trial)\u001b[0m\n\u001b[1;32m 38\u001b[0m fit_features_together=True)\n\u001b[1;32m 39\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 40\u001b[0;31m recos = model.recommend(\n\u001b[0m\u001b[1;32m 41\u001b[0m \u001b[0musers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mTEST_USERS\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 42\u001b[0m \u001b[0mdataset\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/rectools/models/base.py\u001b[0m in \u001b[0;36mrecommend\u001b[0;34m(self, users, dataset, k, filter_viewed, items_to_recommend, add_rank_col)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[0msorted_item_ids_to_recommend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 132\u001b[0;31m reco_user_ids, reco_item_ids, reco_scores = self._recommend_u2i(\n\u001b[0m\u001b[1;32m 133\u001b[0m \u001b[0muser_ids\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 134\u001b[0m \u001b[0mdataset\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/rectools/models/vector.py\u001b[0m in \u001b[0;36m_recommend_u2i\u001b[0;34m(self, user_ids, dataset, k, filter_viewed, sorted_item_ids_to_recommend)\u001b[0m\n\u001b[1;32m 139\u001b[0m \u001b[0mall_scores\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mtp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mList\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 140\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mtarget_id\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtqdm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0muser_ids\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdisable\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mverbose\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 141\u001b[0;31m \u001b[0mscores\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mscores_calculator\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcalc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtarget_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 142\u001b[0m reco_ids, reco_scores = recommend_from_scores(\n\u001b[1;32m 143\u001b[0m \u001b[0mscores\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mscores\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/anaconda3/lib/python3.8/site-packages/rectools/models/vector.py\u001b[0m in \u001b[0;36mcalc\u001b[0;34m(self, subject_id)\u001b[0m\n\u001b[1;32m 96\u001b[0m \u001b[0msubject_factors\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msubjects_factors\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0msubject_id\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdistance\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mDistance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mDOT\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 98\u001b[0;31m \u001b[0mscores\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mobjects_factors\u001b[0m \u001b[0;34m@\u001b[0m \u001b[0msubject_factors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 99\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdistance\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mDistance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mEUCLIDEAN\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 100\u001b[0m \u001b[0msubject_dot\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msubjects_dots\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0msubject_id\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "def objective(trial):\n", + " model_name = trial.suggest_categorical('recomender', ['LightFM',\n", + " 'ALS'])\n", + " if model_name == 'LightFM':\n", + " learning_rate = trial.suggest_float('learning_rate', 1e-10, 1)\n", + " k = trial.suggest_int('k', 2, 10)\n", + " loss = trial.suggest_categorical('loss', ['logistic', 'bpr',\n", + " 'warp'])\n", + " model = LightFMWrapperModel(LightFM(k=k,\n", + " learning_rate=learning_rate,\n", + " loss=loss, no_components=10),\n", + " epochs=N_EPOCHS,\n", + " num_threads=NUM_THREADS)\n", + " else:\n", + " regularization = trial.suggest_float('regularization', 1e-4,\n", + " 1e-2)\n", + " model = \\\n", + " ImplicitALSWrapperModel(model=AlternatingLeastSquares(factors=10,\n", + " random_state=RANDOM_STATE,\n", + " num_threads=NUM_THREADS,\n", + " regularization=regularization),\n", + " fit_features_together=True)\n", + " model.fit(dataset)\n", + " recos = model.recommend(users=TEST_USERS, dataset=dataset,\n", + " k=K_RECOS, filter_viewed=True)\n", + " metric = calc_metrics(metrics, recos, test, train)['MAP@10']\n", + " print ('MAP@10', metric)\n", + " return metric\n", + "\n", + "\n", + "study = optuna.create_study(direction='maximize')\n", + "study.optimize(objective, n_trials=100)" + ] + }, + { + "cell_type": "markdown", + "id": "3989645b", + "metadata": {}, + "source": [ + "## Пришлось остановить обучение, тк эти 30 эпох обучались 6 часов" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "99aae61e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'recomender': 'ALS', 'regularization': 0.00026970098377155163}" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "study.best_params" + ] + }, + { + "cell_type": "code", + "execution_count": 233, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "REGULARIZATION = 0.00026970098377155163" + ] + }, + { + "cell_type": "code", + "execution_count": 234, + "id": "d1f6069a", + "metadata": {}, + "outputs": [], + "source": [ + "# обучаем best model после optuna\n", + "model = ImplicitALSWrapperModel(\n", + " model=AlternatingLeastSquares(\n", + " factors=32,\n", + " random_state=RANDOM_STATE,\n", + " num_threads=NUM_THREADS,\n", + " regularization=REGULARIZATION\n", + " ),\n", + " fit_features_together=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 236, + "id": "d3907c1d", + "metadata": {}, + "outputs": [], + "source": [ + "model.fit(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2050a6e", + "metadata": {}, + "outputs": [], + "source": [ + "recos = model.recommend(\n", + " users=TEST_USERS,\n", + " dataset=dataset,\n", + " k=K_RECOS,\n", + " filter_viewed=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "6bbe3ff3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.07124010116092815" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "calc_metrics(metrics, recos, test, train)['MAP@10']" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "d3eead34", + "metadata": {}, + "outputs": [], + "source": [ + "recos = recos[['user_id', 'item_id']]\n", + "recos.to_csv('ALS_after_optuna.csv.gz', index=False, compression='gzip')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "# Добавление аватаров\n", + "Добавить 3 \"аватаров\" (искусственных пользователей) и посмотреть рекомендации итоговой модели на них. Объяснить почему добавили именно таких пользователей. **(3 балла)**" + ] + }, + { + "cell_type": "code", + "execution_count": 264, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "some_avatars = pd.DataFrame(\n", + " {\n", + " Columns.User: [11111111, 11111112, 11111113],\n", + " Columns.Item: [\n", + " [10732, 3369, 15885], # Аватар, который любит мультики [губка боб, кунг-фу панда, ]\n", + " [3506, 1107, 11235], # Аватар, который любит боевики [форсаж, обитель зла, плохие парни]\n", + " [931, 389, 8315] # Аватар, который любит драмы [после, хатико, сумерки]\n", + " ],\n", + " 'last_watch_dt': [\n", + " [random.choice(sorted(interactions.last_watch_dt.unique())[-10:]) for _ in range(3)] for i in range(3)\n", + " ],\n", + " \"total_dur\": [\n", + " [34343, 6000, 9000],\n", + " [5000, 45452, 9000],\n", + " [5000, 3200, 3232]\n", + " ],\n", + " \"watched_pct\": [\n", + " [100.0, 95.0, 99.0],\n", + " [67.0, 95.0, 99.0],\n", + " [100.0, 53.0, 99.0]\n", + " ],\n", + " Columns.Weight: [\n", + " [5, 5, 5],\n", + " [3, 5, 5],\n", + " [5, 2, 5]\n", + " ]\n", + " }\n", + ")\n", + "\n", + "some_avatars = some_avatars.explode(\n", + " [Columns.Item, Columns.Datetime, \"total_dur\", \"watched_pct\", Columns.Weight]\n", + ").reset_index(drop=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 265, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idlast_watch_dttotal_durwatched_pctweight
011111111107322021-08-2234343100.05
11111111133692021-08-19600095.05
211111111158852021-08-17900099.05
31111111235062021-08-20500067.03
41111111211072021-08-144545295.05
511111112112352021-08-19900099.05
6111111139312021-08-145000100.05
7111111133892021-08-19320053.02
81111111383152021-08-22323299.05
\n", + "
" + ], + "text/plain": [ + " user_id item_id last_watch_dt total_dur watched_pct weight\n", + "0 11111111 10732 2021-08-22 34343 100.0 5\n", + "1 11111111 3369 2021-08-19 6000 95.0 5\n", + "2 11111111 15885 2021-08-17 9000 99.0 5\n", + "3 11111112 3506 2021-08-20 5000 67.0 3\n", + "4 11111112 1107 2021-08-14 45452 95.0 5\n", + "5 11111112 11235 2021-08-19 9000 99.0 5\n", + "6 11111113 931 2021-08-14 5000 100.0 5\n", + "7 11111113 389 2021-08-19 3200 53.0 2\n", + "8 11111113 8315 2021-08-22 3232 99.0 5" + ] + }, + "execution_count": 265, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "some_avatars" + ] + }, + { + "cell_type": "code", + "execution_count": 266, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "interactions_with_avatars = pd.concat([interactions, some_avatars])\n", + "\n", + "dataset_with_avatars = Dataset.construct(\n", + " interactions_df=interactions_with_avatars,\n", + ")\n", + "\n", + "# обучаем best model после optuna\n", + "model_with_avatars = ImplicitALSWrapperModel(\n", + " model=AlternatingLeastSquares(\n", + " factors=32,\n", + " random_state=RANDOM_STATE,\n", + " num_threads=NUM_THREADS,\n", + " regularization=REGULARIZATION\n", + " ),\n", + " fit_features_together=True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 267, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 267, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model_with_avatars.fit(dataset_with_avatars)" + ] + }, + { + "cell_type": "code", + "execution_count": 268, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idscorerank
011111111119850.0407001
11111111129540.0394662
21111111173100.0365913
31111111131820.0354234
41111111144750.0344225
51111111277930.0356391
61111111247020.0351392
711111112163610.0348573
81111111212870.0343274
911111112110180.0323495
101111111319160.2456551
1111111113144700.2427862
12111111131010.2192313
1311111113124630.2066154
141111111357320.1830125
\n", + "
" + ], + "text/plain": [ + " user_id item_id score rank\n", + "0 11111111 11985 0.040700 1\n", + "1 11111111 2954 0.039466 2\n", + "2 11111111 7310 0.036591 3\n", + "3 11111111 3182 0.035423 4\n", + "4 11111111 4475 0.034422 5\n", + "5 11111112 7793 0.035639 1\n", + "6 11111112 4702 0.035139 2\n", + "7 11111112 16361 0.034857 3\n", + "8 11111112 1287 0.034327 4\n", + "9 11111112 11018 0.032349 5\n", + "10 11111113 1916 0.245655 1\n", + "11 11111113 14470 0.242786 2\n", + "12 11111113 101 0.219231 3\n", + "13 11111113 12463 0.206615 4\n", + "14 11111113 5732 0.183012 5" + ] + }, + "execution_count": 268, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "recos_with_avatars = model_with_avatars.recommend(\n", + " users=[11111111, 11111112, 11111113],\n", + " dataset=dataset_with_avatars,\n", + " k=5,\n", + " filter_viewed=True\n", + ")\n", + "recos_with_avatars" + ] + }, + { + "cell_type": "code", + "execution_count": 269, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_idscorerankcontent_typetitletitle_origrelease_yeargenrescountriesfor_kidsage_ratingstudiosdirectorsactorsdescriptionkeywordsgenre
011111111119850.0407001filmИстория игрушек 4Toy Story 42019.0мультфильм, фэнтези, комедииСШАNaN6.0NaNДжош Кули[том хэнкс, тим аллен, энни поттс, тони хейл, ...Космический рейнджер Баз Лайтер, ковбой Вуди, ...[игрушка, дружба, ковбой, история игрушек 4, ,...[мультфильм, фэнтези, комедии]
11111111129540.0394662filmМиньоныMinions2015.0фантастика, мультфильм, приключения, комедииСШАNaN6.0NaNКайл Балда, Пьер Коффан[сандра буллок, джон хэмм, майкл китон, эллисо...Миньоны живут на планете гораздо дольше нас. У...[помощник, сцена после титров, сцена во время ...[фантастика, мультфильм, приключения, комедии]
21111111173100.0365913filmГадкий я 2Despicable Me 22013.0мультфильм, приключения, фантастика, фэнтези, ...США, Франция, ЯпонияNaN0.0NaNПьер Коффан, Крис Рено[стив карелл, кристен уиг, бенджамин брэтт, ми...В то время как Грю, бывший суперзлодей, приспо...[отношения родитель-ребенок, секретный агент, ...[мультфильм, приключения, фантастика, фэнтези,...
31111111131820.0354234filmРальф против ИнтернетаRalph Breaks the Internet2018.0мультфильм, приключения, фантастика, семейное,...СШАNaN6.0NaNРич Мур, Фил Джонстон[джон си райли, сара силверман, галь гадот, та...На этот раз Ральф и Ванилопа фон Кекс выйдут з...[видеоигра, мультфильм, продолжение, интернет,...[мультфильм, приключения, фантастика, семейное...
41111111144750.0344225filmТачкиCars2006.0спорт, мультфильм, комедииСШАNaN6.0NaNДжон Лассетер, Джо Рэнфт[оуэн уилсон, пол ньюман, бонни хант, ларри-ка...Неукротимый в своем желании всегда и во всем п...[автомобильная гонка, трасса 66, porsche, выхо...[спорт, мультфильм, комедии]
51111111277930.0356391filmРадиовспышкаRadioflash2019.0боевики, драмы, фантастика, триллерыСШАNaN16.0NaNБен Макферсон[брайтон шарбино, доминик монахэн, уилл пэттон...Риз с легкостью проходит виртуальные квесты, н...[2019, соединенные штаты, радиовспышка][боевики, драмы, фантастика, триллеры]
61111111247020.0351392filmХищникPredator1987.0боевики, фантастика, триллеры, приключенияСША, МексикаNaN16.0NaNДжон МакТирнан[арнольд шварценеггер, карл уэзерс, эльпидия к...Американский вертолет был сбит партизанами в Ю...[центральная и южная америка, хищник, иноплане...[боевики, фантастика, триллеры, приключения]
711111112163610.0348573filmDoom: АннигиляцияDoom: Annihilation2019.0боевики, ужасы, фантастика, триллерыСШАNaN18.0NaNТони Гиглио[эми мэнсон, доминик мафэм, люк аллен-гейл, дж...Отряд морпехов прилетает на марсианский спутни...[планета марс, ад, космос, демон, по мотивам в...[боевики, ужасы, фантастика, триллеры]
81111111212870.0343274filmТерминатор: Тёмные судьбыTerminator: Dark Fate2019.0боевики, фантастика, приключенияСША, КитайNaN16.0NaNТим Миллер[линда хэмилтон, арнольд шварценеггер, маккенз...Мексика. Милая девушка Даниэла Рамос, а для др...[искуственный интеллект, киборг, вертолет, мех...[боевики, фантастика, приключения]
911111112110180.0323495filmХищникиPredators2010.0боевики, фантастика, триллеры, приключенияСШАNaN16.0NaNНимрод Антал[эдриан броуди, тофер грейс, алиси брага, уолт...Наемник Ройс невольно вынужден возглавить груп...[охотник, хищник, якудза, охота на людей, иноп...[боевики, фантастика, триллеры, приключения]
101111111319160.2456551filmСекс и ничего лишнегоMy Awkward Sexual Adventure2012.0мелодрамыКанадаNaN18.0NaNШон Гэррити[джонас черник, эмили хэмпшир, сара мэннинен, ...Чтобы вернуть свою неудовлетворенную бывшую де...[нижнее белье, массажистка, секс втроем, фалло...[мелодрамы]
1111111113144700.2427862filmЛюбовьLove2015.0драмы, мелодрамыФранцияNaN18.0NaNГаспар Ноэ[аоми муйок, карл глусман, клара кристин, уго ...Любовь вне добра и зла. Любовь — это генетичес...[париж, франция, секс, сексуальность, галерея,...[драмы, мелодрамы]
12111111131010.2192313filmКуриосаCuriosa2019.0историческое, мелодрамыФранцияNaN18.0NaNЛу Жене[ноэми мерлан, нильс шнайдер, бенжамен лаверн,...Историческая драма про любовный треугольник, к...[, 1890-е, большой пенис, брак без секса, вдох...[историческое, мелодрамы]
1311111113124630.2066154filmСтудентка по вызовуMes chères études2010.0драмы, мелодрамыФранцияNaN18.0NaNЭмманюэль Берко[дебора франсуа, ален коши, матье деми, бенжам...Лаура — 19-летняя первокурсница французского у...[франция, гостиница, по роману или книге, гост...[драмы, мелодрамы]
141111111357320.1830125filmТайное влечениеAdore2013.0драмы, мелодрамыАвстралия, ФранцияNaN16.0NaNАнн Фонтен[наоми уоттс, робин райт, завьер сэмюэл, джейм...Главные героини картины – Лил и Роз — две давн...[пляж, нагота, любовники, месть, лучший друг, ...[драмы, мелодрамы]
\n", + "
" + ], + "text/plain": [ + " user_id item_id score rank content_type title \\\n", + "0 11111111 11985 0.040700 1 film История игрушек 4 \n", + "1 11111111 2954 0.039466 2 film Миньоны \n", + "2 11111111 7310 0.036591 3 film Гадкий я 2 \n", + "3 11111111 3182 0.035423 4 film Ральф против Интернета \n", + "4 11111111 4475 0.034422 5 film Тачки \n", + "5 11111112 7793 0.035639 1 film Радиовспышка \n", + "6 11111112 4702 0.035139 2 film Хищник \n", + "7 11111112 16361 0.034857 3 film Doom: Аннигиляция \n", + "8 11111112 1287 0.034327 4 film Терминатор: Тёмные судьбы \n", + "9 11111112 11018 0.032349 5 film Хищники \n", + "10 11111113 1916 0.245655 1 film Секс и ничего лишнего \n", + "11 11111113 14470 0.242786 2 film Любовь \n", + "12 11111113 101 0.219231 3 film Куриоса \n", + "13 11111113 12463 0.206615 4 film Студентка по вызову \n", + "14 11111113 5732 0.183012 5 film Тайное влечение \n", + "\n", + " title_orig release_year \\\n", + "0 Toy Story 4 2019.0 \n", + "1 Minions 2015.0 \n", + "2 Despicable Me 2 2013.0 \n", + "3 Ralph Breaks the Internet 2018.0 \n", + "4 Cars 2006.0 \n", + "5 Radioflash 2019.0 \n", + "6 Predator 1987.0 \n", + "7 Doom: Annihilation 2019.0 \n", + "8 Terminator: Dark Fate 2019.0 \n", + "9 Predators 2010.0 \n", + "10 My Awkward Sexual Adventure 2012.0 \n", + "11 Love 2015.0 \n", + "12 Curiosa 2019.0 \n", + "13 Mes chères études 2010.0 \n", + "14 Adore 2013.0 \n", + "\n", + " genres countries \\\n", + "0 мультфильм, фэнтези, комедии США \n", + "1 фантастика, мультфильм, приключения, комедии США \n", + "2 мультфильм, приключения, фантастика, фэнтези, ... США, Франция, Япония \n", + "3 мультфильм, приключения, фантастика, семейное,... США \n", + "4 спорт, мультфильм, комедии США \n", + "5 боевики, драмы, фантастика, триллеры США \n", + "6 боевики, фантастика, триллеры, приключения США, Мексика \n", + "7 боевики, ужасы, фантастика, триллеры США \n", + "8 боевики, фантастика, приключения США, Китай \n", + "9 боевики, фантастика, триллеры, приключения США \n", + "10 мелодрамы Канада \n", + "11 драмы, мелодрамы Франция \n", + "12 историческое, мелодрамы Франция \n", + "13 драмы, мелодрамы Франция \n", + "14 драмы, мелодрамы Австралия, Франция \n", + "\n", + " for_kids age_rating studios directors \\\n", + "0 NaN 6.0 NaN Джош Кули \n", + "1 NaN 6.0 NaN Кайл Балда, Пьер Коффан \n", + "2 NaN 0.0 NaN Пьер Коффан, Крис Рено \n", + "3 NaN 6.0 NaN Рич Мур, Фил Джонстон \n", + "4 NaN 6.0 NaN Джон Лассетер, Джо Рэнфт \n", + "5 NaN 16.0 NaN Бен Макферсон \n", + "6 NaN 16.0 NaN Джон МакТирнан \n", + "7 NaN 18.0 NaN Тони Гиглио \n", + "8 NaN 16.0 NaN Тим Миллер \n", + "9 NaN 16.0 NaN Нимрод Антал \n", + "10 NaN 18.0 NaN Шон Гэррити \n", + "11 NaN 18.0 NaN Гаспар Ноэ \n", + "12 NaN 18.0 NaN Лу Жене \n", + "13 NaN 18.0 NaN Эмманюэль Берко \n", + "14 NaN 16.0 NaN Анн Фонтен \n", + "\n", + " actors \\\n", + "0 [том хэнкс, тим аллен, энни поттс, тони хейл, ... \n", + "1 [сандра буллок, джон хэмм, майкл китон, эллисо... \n", + "2 [стив карелл, кристен уиг, бенджамин брэтт, ми... \n", + "3 [джон си райли, сара силверман, галь гадот, та... \n", + "4 [оуэн уилсон, пол ньюман, бонни хант, ларри-ка... \n", + "5 [брайтон шарбино, доминик монахэн, уилл пэттон... \n", + "6 [арнольд шварценеггер, карл уэзерс, эльпидия к... \n", + "7 [эми мэнсон, доминик мафэм, люк аллен-гейл, дж... \n", + "8 [линда хэмилтон, арнольд шварценеггер, маккенз... \n", + "9 [эдриан броуди, тофер грейс, алиси брага, уолт... \n", + "10 [джонас черник, эмили хэмпшир, сара мэннинен, ... \n", + "11 [аоми муйок, карл глусман, клара кристин, уго ... \n", + "12 [ноэми мерлан, нильс шнайдер, бенжамен лаверн,... \n", + "13 [дебора франсуа, ален коши, матье деми, бенжам... \n", + "14 [наоми уоттс, робин райт, завьер сэмюэл, джейм... \n", + "\n", + " description \\\n", + "0 Космический рейнджер Баз Лайтер, ковбой Вуди, ... \n", + "1 Миньоны живут на планете гораздо дольше нас. У... \n", + "2 В то время как Грю, бывший суперзлодей, приспо... \n", + "3 На этот раз Ральф и Ванилопа фон Кекс выйдут з... \n", + "4 Неукротимый в своем желании всегда и во всем п... \n", + "5 Риз с легкостью проходит виртуальные квесты, н... \n", + "6 Американский вертолет был сбит партизанами в Ю... \n", + "7 Отряд морпехов прилетает на марсианский спутни... \n", + "8 Мексика. Милая девушка Даниэла Рамос, а для др... \n", + "9 Наемник Ройс невольно вынужден возглавить груп... \n", + "10 Чтобы вернуть свою неудовлетворенную бывшую де... \n", + "11 Любовь вне добра и зла. Любовь — это генетичес... \n", + "12 Историческая драма про любовный треугольник, к... \n", + "13 Лаура — 19-летняя первокурсница французского у... \n", + "14 Главные героини картины – Лил и Роз — две давн... \n", + "\n", + " keywords \\\n", + "0 [игрушка, дружба, ковбой, история игрушек 4, ,... \n", + "1 [помощник, сцена после титров, сцена во время ... \n", + "2 [отношения родитель-ребенок, секретный агент, ... \n", + "3 [видеоигра, мультфильм, продолжение, интернет,... \n", + "4 [автомобильная гонка, трасса 66, porsche, выхо... \n", + "5 [2019, соединенные штаты, радиовспышка] \n", + "6 [центральная и южная америка, хищник, иноплане... \n", + "7 [планета марс, ад, космос, демон, по мотивам в... \n", + "8 [искуственный интеллект, киборг, вертолет, мех... \n", + "9 [охотник, хищник, якудза, охота на людей, иноп... \n", + "10 [нижнее белье, массажистка, секс втроем, фалло... \n", + "11 [париж, франция, секс, сексуальность, галерея,... \n", + "12 [, 1890-е, большой пенис, брак без секса, вдох... \n", + "13 [франция, гостиница, по роману или книге, гост... \n", + "14 [пляж, нагота, любовники, месть, лучший друг, ... \n", + "\n", + " genre \n", + "0 [мультфильм, фэнтези, комедии] \n", + "1 [фантастика, мультфильм, приключения, комедии] \n", + "2 [мультфильм, приключения, фантастика, фэнтези,... \n", + "3 [мультфильм, приключения, фантастика, семейное... \n", + "4 [спорт, мультфильм, комедии] \n", + "5 [боевики, драмы, фантастика, триллеры] \n", + "6 [боевики, фантастика, триллеры, приключения] \n", + "7 [боевики, ужасы, фантастика, триллеры] \n", + "8 [боевики, фантастика, приключения] \n", + "9 [боевики, фантастика, триллеры, приключения] \n", + "10 [мелодрамы] \n", + "11 [драмы, мелодрамы] \n", + "12 [историческое, мелодрамы] \n", + "13 [драмы, мелодрамы] \n", + "14 [драмы, мелодрамы] " + ] + }, + "execution_count": 269, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "recos_with_avatars.merge(items, on='item_id', how='left')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "В целом, все рекомендации к каждому аватару получились неплохие" + ] + }, + { + "cell_type": "markdown", + "id": "ab12c89f", + "metadata": {}, + "source": [ + " # Метод приближенного поиска соседей для выдачи рекомендаций.\n", + "\n", + "Воспользоваться методом приближенного поиска соседей для выдачи рекомендаций. **(3 балла)**\n", + "Можно использовать любые удобные: [`Annoy`](https://github.com/spotify/annoy), [`nmslib`](https://github.com/nmslib/nmslib) и.т.д" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "5d1161b4", + "metadata": {}, + "outputs": [], + "source": [ + "LEARNING_RATE = 0.08165160206425184\n", + "LOSS = 'warp'" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "9f20ae6e", + "metadata": {}, + "outputs": [], + "source": [ + "# обучаем LightFM, чтобы достать из нее вектора пользователей и айтемов\n", + "model = LightFMWrapperModel(\n", + " LightFM(\n", + " k=K,\n", + " no_components=NO_COMPONENTS,\n", + " loss= LOSS,\n", + " random_state=RANDOM_STATE,\n", + " learning_rate=LEARNING_RATE,\n", + " user_alpha=USER_ALPHA,\n", + " item_alpha=ITEM_ALPHA\n", + " ),\n", + " epochs=N_EPOCHS,\n", + " num_threads=NUM_THREADS\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "9982d55c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.fit(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "348b4fd6", + "metadata": {}, + "outputs": [], + "source": [ + "user_embeddings, item_embeddings = model.get_vectors(dataset)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "fd296cf4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((756545, 34), (13963, 34))" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "user_embeddings.shape, item_embeddings.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "d75b4570", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([-2.48910075e+02, 1.00000000e+00, -3.04745167e-01, 1.30259299e-01,\n", + " -1.25937782e-01, 1.32810516e-01, -3.98682870e-01, 2.12211904e-01,\n", + " 3.15063161e-01, 9.48599975e-03, 1.79876286e-01, -1.15210674e-01,\n", + " 3.13044085e-01, 5.25500635e-02, -8.94866249e-02, 6.32450803e-02,\n", + " 3.30464664e-01, -2.79290127e-01, 7.66675368e-02, 2.69704125e-01,\n", + " -2.02872234e-01, 3.65543869e-02, -2.08750917e-01, -7.48397237e-02,\n", + " 2.09714412e-01, 1.28161004e-01, -2.49842146e-01, 7.57336145e-02,\n", + " 6.74582969e-02, 2.03054725e-01, -4.46441335e-02, 1.00222239e-01,\n", + " 3.09746759e-01, 2.26963989e-01])" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#пользователь со средним значением по каждому признаку\n", + "first_person = user_embeddings.mean(0)\n", + "first_person" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "88198015", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([-364.41271973, 1. , -2.19337273, -2.05960441,\n", + " -3.03305483, -2.15237951, -3.10027146, -1.44008625,\n", + " -1.55592728, -1.69499218, -1.87335801, -3.19125104,\n", + " -1.8664068 , -2.62357092, -2.23808742, -1.78050435,\n", + " -1.67308342, -2.70261121, -1.94637215, -2.44766903,\n", + " -2.40233946, -1.93648934, -2.32535124, -2.73280096,\n", + " -2.20278382, -1.93085539, -2.29366779, -1.9847883 ,\n", + " -2.619452 , -1.8447926 , -2.03915739, -1.86472845,\n", + " -1.80441463, -1.98137343])" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#пользователь со минимальным значением по каждому признаку\n", + "second_person = user_embeddings.min(axis=0)\n", + "second_person" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "4e02dec3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0. , 1. , 1.80414879, 2.91568851, 2.57092023,\n", + " 2.49563074, 3.0165813 , 2.02071786, 2.10402703, 1.86775827,\n", + " 1.81193578, 2.3456769 , 2.37611055, 4.0926795 , 1.8776809 ,\n", + " 1.91333187, 2.16625547, 1.76979506, 1.87265527, 2.63360167,\n", + " 1.97114313, 1.88249838, 1.89424872, 2.15507913, 2.38049865,\n", + " 1.99921763, 1.80478072, 1.8311218 , 3.32906055, 2.25318193,\n", + " 2.0245254 , 2.37490654, 2.28989363, 1.97282732])" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#пользователь со максимальным значением по каждому признаку\n", + "third_person = user_embeddings.max(axis=0)\n", + "third_person" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "75a314ac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(756545, 34)" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "user_embeddings.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "3cb72431", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(756548, 34)" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# добавим векотра 3х новых пользователей\n", + "user_embeddings = np.append(user_embeddings, np.array([first_person,\n", + " second_person, third_person]), axis=0)\n", + "user_embeddings.shape\n" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "db53d401", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index-time parameters {'M': 50, 'indexThreadQty': 8, 'efConstruction': 150, 'post': 0, 'chunkIndexSize': 10000}\n" + ] + } + ], + "source": [ + "M = 50\n", + "efC = 150\n", + "\n", + "num_threads = 8\n", + "chunkIndexSize = 10000\n", + "\n", + "index_time_params = {\n", + " 'M': M,\n", + " 'indexThreadQty': num_threads,\n", + " 'efConstruction': efC,\n", + " 'post': 0,\n", + " 'chunkIndexSize': chunkIndexSize,\n", + " }\n", + "print ('Index-time parameters', index_time_params)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "e09782e5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "13963" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "K = 10\n", + "space_name = 'negdotprod'\n", + "index = nmslib.init(method='hnsw', space=space_name,\n", + " data_type=nmslib.DataType.DENSE_VECTOR)\n", + "index.addDataPointBatch(item_embeddings)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "209d778e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index-time parameters {'M': 50, 'indexThreadQty': 8, 'efConstruction': 150}\n", + "Indexing time = 1.392440\n" + ] + } + ], + "source": [ + "start = time.time()\n", + "index_time_params = {'M': M, 'indexThreadQty': num_threads,\n", + " 'efConstruction': efC}\n", + "index.createIndex(index_time_params)\n", + "end = time.time()\n", + "print('Index-time parameters', index_time_params)\n", + "print('Indexing time = %f' % (end - start))" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "998dad8b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Setting query-time parameters {'efSearch': 100}\n" + ] + } + ], + "source": [ + "efS = 100\n", + "query_time_params = {'efSearch': efS}\n", + "print('Setting query-time parameters', query_time_params)\n", + "index.setQueryTimeParams(query_time_params)" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "6f5cd03c", + "metadata": {}, + "outputs": [], + "source": [ + "query_matrix = user_embeddings" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "77039943", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "kNN time total=3.962551 (sec), per query=0.000005 (sec), per query adjusted for thread number=0.000042 (sec)\n" + ] + } + ], + "source": [ + "query_qty = query_matrix.shape[0]\n", + "start = time.time()\n", + "nbrs = index.knnQueryBatch(query_matrix, k=K, num_threads=num_threads)\n", + "end = time.time()\n", + "print(\n", + " 'kNN time total=%f (sec), per query=%f (sec), per query adjusted for thread number=%f (sec)' \\\n", + " % (end - start, float(end - start) / query_qty, num_threads * float(end - start) / query_qty)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "cac18e0e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 32, 19, 43, ..., 31, 36, 69],\n", + " [ 31, 262, 62, ..., 32, 121, 735],\n", + " [ 19, 31, 121, ..., 29, 62, 105],\n", + " ...,\n", + " [ 31, 19, 43, ..., 268, 100, 86],\n", + " [ 19, 31, 32, ..., 268, 49, 173],\n", + " [ 19, 31, 32, ..., 100, 105, 268]])" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# рекомендации для всех пользователей\n", + "recos_old_persons = np.array(nbrs, dtype=int)[:,0][:len(nbrs) - 3]\n", + "recos_old_persons" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "918c6976", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 31, 19, 32, 43, 62, 121, 173, 268, 100,\n", + " 86],\n", + " [12018, 10998, 7308, 12006, 10159, 10215, 3543, 8118, 8180,\n", + " 11511],\n", + " [ 8453, 6400, 3557, 3945, 6642, 4779, 3583, 2068, 8668,\n", + " 371]])" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# рекомендации для добавленных пользователей\n", + "recos_new_persons = np.array(nbrs, dtype=int)[:,0][-3:]\n", + "recos_new_persons" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "c78fbc6a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_iditem_id
0032
1019
2043
3062
40449
.........
756544575654443
756544675654429
7565447756544100
7565448756544105
7565449756544268
\n", + "

7565450 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " user_id item_id\n", + "0 0 32\n", + "1 0 19\n", + "2 0 43\n", + "3 0 62\n", + "4 0 449\n", + "... ... ...\n", + "7565445 756544 43\n", + "7565446 756544 29\n", + "7565447 756544 100\n", + "7565448 756544 105\n", + "7565449 756544 268\n", + "\n", + "[7565450 rows x 2 columns]" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_r = \\\n", + " pd.DataFrame({'user_id': np.repeat(np.arange(len(recos_old_persons)),\n", + " 10), Columns.Item: recos_old_persons.ravel()})\n", + "df_r\n" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "902cbca6", + "metadata": {}, + "outputs": [], + "source": [ + "df_r.to_csv('nmslib.csv.gz', index=False, compression='gzip')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "# Холодные пользователи\n", + "Придумать как можно обработать рекомендации для холодных пользователей. **(3 балла)**\n", + "\n", + "В предыдущей домашке, мы выяснили, что на этом датасете лучше всего себя ведет топ за последний месяц. Поэтому каждую модель мы использовали с подобным методом добивки холодных пользователей\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "39d02966", + "metadata": {}, + "source": [ + "# For baseline\n", + "Пробитие бейзлайна MAP@10 >= 0.074921 (6 баллов).\n", + "Чтобы пробить бейзлайн, было принято решение обучиться на всем датасете" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f6d0f92", + "metadata": {}, + "outputs": [], + "source": [ + "data = Dataset.construct(\n", + " interactions,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model = ImplicitALSWrapperModel(\n", + " model=AlternatingLeastSquares(\n", + " factors=32,\n", + " random_state=RANDOM_STATE,\n", + " num_threads=NUM_THREADS,\n", + " regularization=REGULARIZATION\n", + " ),\n", + " fit_features_together=True\n", + ")\n", + "\n", + "model.fit(data)\n", + "recos = model.recommend(\n", + " users=TEST_USERS,\n", + " dataset=data,\n", + " k=10,\n", + " filter_viewed=True\n", + ")\n", + "\n", + "recos = recos[['user_id', 'item_id']]\n", + "recos.to_csv('ALSnew.csv.gz', index=False, compression='gzip')" + ] + } + ], + "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.8.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/online_LightFm.ipynb b/notebooks/online_LightFm.ipynb new file mode 100644 index 00000000..444b7730 --- /dev/null +++ b/notebooks/online_LightFm.ipynb @@ -0,0 +1,173 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import pickle\n", + "import time\n", + "from collections import defaultdict\n", + "from functools import reduce\n", + "from pathlib import Path\n", + "from typing import Optional\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy as sp\n", + "from lightfm import LightFM\n", + "from scipy.sparse import csr_matrix\n", + "from tqdm import tqdm\n", + "\n", + "from service.api.models_zoo import LightFMWrapper" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "os.environ['OPENBLAS_NUM_THREADS'] = \"1\"\n", + "datetime_col = 'last_watch_dt'\n", + "DATA_PATH = Path(\"../data/kion_train/\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 2.01 s, sys: 224 ms, total: 2.23 s\n", + "Wall time: 2.24 s\n" + ] + } + ], + "source": [ + "%%time\n", + "users = pd.read_csv(DATA_PATH / 'users.csv')\n", + "items = pd.read_csv(DATA_PATH / 'items.csv')\n", + "interactions = pd.read_csv(DATA_PATH / 'interactions.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "interactions[datetime_col] = pd.to_datetime(interactions[datetime_col], format='%Y-%m-%d')\n", + "interactions.dropna(inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "interactions['watched'] = pd.cut(\n", + " x=interactions['watched_pct'],\n", + " bins=5,\n", + " labels=[1, 2, 3, 4, 5]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Удаляем те юзеры и айтемы, которых нет в основных табличках\n", + "interactions = interactions.merge(users.user_id.drop_duplicates(), on='user_id')\n", + "interactions = interactions.merge(items.item_id.drop_duplicates(), on='item_id')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "model = LightFMWrapper(epochs=5)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch: 100%|██████████| 5/5 [00:27<00:00, 5.43s/it]\n" + ] + } + ], + "source": [ + "model.fit(train=interactions, item_features=items, user_features=users)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[3734, 2657, 11237, 13594, 12248, 11754, 1020, 10242, 142, 1451]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model.predict(user_id=176549)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "with open('../data/lightfm.pickle', 'wb') as f:\n", + " pickle.dump(model, f)" + ] + } + ], + "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.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/poetry.lock b/poetry.lock index 5d7751c6..1fbd1d87 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,13 +1,19 @@ +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + [[package]] name = "asgiref" -version = "3.5.2" +version = "3.6.0" description = "ASGI specs, helper code, and adapters" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"}, + {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"}, +] [package.extras] -tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] [[package]] name = "astroid" @@ -16,6 +22,10 @@ description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false python-versions = "~=3.6" +files = [ + {file = "astroid-2.5.6-py3-none-any.whl", hash = "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e"}, + {file = "astroid-2.5.6.tar.gz", hash = "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"}, +] [package.dependencies] lazy-object-proxy = ">=1.4.0" @@ -28,55 +38,76 @@ description = "Atomic file writes." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] [[package]] name = "attrs" -version = "21.4.0" +version = "22.2.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" +files = [ + {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, + {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, +] [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] +tests = ["attrs[tests-no-zope]", "zope.interface"] +tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] [[package]] name = "bandit" -version = "1.7.4" +version = "1.7.5" description = "Security oriented static analyser for python code." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, + {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, +] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} GitPython = ">=1.0.1" PyYAML = ">=5.3.1" +rich = "*" stevedore = ">=1.20.0" [package.extras] -test = ["coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml", "beautifulsoup4 (>=4.8.0)", "pylint (==1.9.4)"] -toml = ["toml"] -yaml = ["pyyaml"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +toml = ["tomli (>=1.1.0)"] +yaml = ["PyYAML"] [[package]] name = "certifi" -version = "2022.6.15" +version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "dev" +category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] [[package]] name = "chardet" version = "4.0.0" description = "Universal encoding detector for Python 2 and 3" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] [[package]] name = "click" @@ -85,17 +116,112 @@ description = "Composable command line interface toolkit" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "colorama" -version = "0.4.5" +version = "0.4.6" description = "Cross-platform colored terminal text." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "contourpy" +version = "1.0.7" +description = "Python library for calculating contours of 2D quadrilateral grids" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95c3acddf921944f241b6773b767f1cbce71d03307270e2d769fd584d5d1092d"}, + {file = "contourpy-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc1464c97579da9f3ab16763c32e5c5d5bb5fa1ec7ce509a4ca6108b61b84fab"}, + {file = "contourpy-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8acf74b5d383414401926c1598ed77825cd530ac7b463ebc2e4f46638f56cce6"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c71fdd8f1c0f84ffd58fca37d00ca4ebaa9e502fb49825484da075ac0b0b803"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99e9486bf1bb979d95d5cffed40689cb595abb2b841f2991fc894b3452290e8"}, + {file = "contourpy-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f4d8941a9564cda3f7fa6a6cd9b32ec575830780677932abdec7bcb61717b0"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9e20e5a1908e18aaa60d9077a6d8753090e3f85ca25da6e25d30dc0a9e84c2c6"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a877ada905f7d69b2a31796c4b66e31a8068b37aa9b78832d41c82fc3e056ddd"}, + {file = "contourpy-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6381fa66866b0ea35e15d197fc06ac3840a9b2643a6475c8fff267db8b9f1e69"}, + {file = "contourpy-1.0.7-cp310-cp310-win32.whl", hash = "sha256:3c184ad2433635f216645fdf0493011a4667e8d46b34082f5a3de702b6ec42e3"}, + {file = "contourpy-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:3caea6365b13119626ee996711ab63e0c9d7496f65641f4459c60a009a1f3e80"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed33433fc3820263a6368e532f19ddb4c5990855e4886088ad84fd7c4e561c71"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38e2e577f0f092b8e6774459317c05a69935a1755ecfb621c0a98f0e3c09c9a5"}, + {file = "contourpy-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae90d5a8590e5310c32a7630b4b8618cef7563cebf649011da80874d0aa8f414"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130230b7e49825c98edf0b428b7aa1125503d91732735ef897786fe5452b1ec2"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58569c491e7f7e874f11519ef46737cea1d6eda1b514e4eb5ac7dab6aa864d02"}, + {file = "contourpy-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d43960d809c4c12508a60b66cb936e7ed57d51fb5e30b513934a4a23874fae"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:152fd8f730c31fd67fe0ffebe1df38ab6a669403da93df218801a893645c6ccc"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9056c5310eb1daa33fc234ef39ebfb8c8e2533f088bbf0bc7350f70a29bde1ac"}, + {file = "contourpy-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a9d7587d2fdc820cc9177139b56795c39fb8560f540bba9ceea215f1f66e1566"}, + {file = "contourpy-1.0.7-cp311-cp311-win32.whl", hash = "sha256:4ee3ee247f795a69e53cd91d927146fb16c4e803c7ac86c84104940c7d2cabf0"}, + {file = "contourpy-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:5caeacc68642e5f19d707471890f037a13007feba8427eb7f2a60811a1fc1350"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd7dc0e6812b799a34f6d12fcb1000539098c249c8da54f3566c6a6461d0dbad"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f9d350b639db6c2c233d92c7f213d94d2e444d8e8fc5ca44c9706cf72193772"}, + {file = "contourpy-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e96a08b62bb8de960d3a6afbc5ed8421bf1a2d9c85cc4ea73f4bc81b4910500f"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:031154ed61f7328ad7f97662e48660a150ef84ee1bc8876b6472af88bf5a9b98"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e9ebb4425fc1b658e13bace354c48a933b842d53c458f02c86f371cecbedecc"}, + {file = "contourpy-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efb8f6d08ca7998cf59eaf50c9d60717f29a1a0a09caa46460d33b2924839dbd"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6c180d89a28787e4b73b07e9b0e2dac7741261dbdca95f2b489c4f8f887dd810"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b8d587cc39057d0afd4166083d289bdeff221ac6d3ee5046aef2d480dc4b503c"}, + {file = "contourpy-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:769eef00437edf115e24d87f8926955f00f7704bede656ce605097584f9966dc"}, + {file = "contourpy-1.0.7-cp38-cp38-win32.whl", hash = "sha256:62398c80ef57589bdbe1eb8537127321c1abcfdf8c5f14f479dbbe27d0322e66"}, + {file = "contourpy-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:57119b0116e3f408acbdccf9eb6ef19d7fe7baf0d1e9aaa5381489bc1aa56556"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30676ca45084ee61e9c3da589042c24a57592e375d4b138bd84d8709893a1ba4"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e927b3868bd1e12acee7cc8f3747d815b4ab3e445a28d2e5373a7f4a6e76ba1"}, + {file = "contourpy-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:366a0cf0fc079af5204801786ad7a1c007714ee3909e364dbac1729f5b0849e5"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ba9bb365446a22411f0673abf6ee1fea3b2cf47b37533b970904880ceb72f3"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71b0bf0c30d432278793d2141362ac853859e87de0a7dee24a1cea35231f0d50"}, + {file = "contourpy-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7281244c99fd7c6f27c1c6bfafba878517b0b62925a09b586d88ce750a016d2"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6d0f9e1d39dbfb3977f9dd79f156c86eb03e57a7face96f199e02b18e58d32a"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f6979d20ee5693a1057ab53e043adffa1e7418d734c1532e2d9e915b08d8ec2"}, + {file = "contourpy-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dd34c1ae752515318224cba7fc62b53130c45ac6a1040c8b7c1a223c46e8967"}, + {file = "contourpy-1.0.7-cp39-cp39-win32.whl", hash = "sha256:c5210e5d5117e9aec8c47d9156d1d3835570dd909a899171b9535cb4a3f32693"}, + {file = "contourpy-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:60835badb5ed5f4e194a6f21c09283dd6e007664a86101431bf870d9e86266c4"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ce41676b3d0dd16dbcfabcc1dc46090aaf4688fd6e819ef343dbda5a57ef0161"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a011cf354107b47c58ea932d13b04d93c6d1d69b8b6dce885e642531f847566"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:31a55dccc8426e71817e3fe09b37d6d48ae40aae4ecbc8c7ad59d6893569c436"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69f8ff4db108815addd900a74df665e135dbbd6547a8a69333a68e1f6e368ac2"}, + {file = "contourpy-1.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efe99298ba37e37787f6a2ea868265465410822f7bea163edcc1bd3903354ea9"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a1e97b86f73715e8670ef45292d7cc033548266f07d54e2183ecb3c87598888f"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc331c13902d0f50845099434cd936d49d7a2ca76cb654b39691974cb1e4812d"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24847601071f740837aefb730e01bd169fbcaa610209779a78db7ebb6e6a7051"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abf298af1e7ad44eeb93501e40eb5a67abbf93b5d90e468d01fc0c4451971afa"}, + {file = "contourpy-1.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:64757f6460fc55d7e16ed4f1de193f362104285c667c112b50a804d482777edd"}, + {file = "contourpy-1.0.7.tar.gz", hash = "sha256:d8165a088d31798b59e91117d1f5fc3df8168d8b48c4acc10fc0df0d0bdbcc5e"}, +] + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "chromedriver", "selenium"] +docs = ["furo", "sphinx-copybutton"] +mypy = ["contourpy[bokeh]", "docutils-stubs", "mypy (==0.991)", "types-Pillow"] +test = ["Pillow", "matplotlib", "pytest"] +test-no-images = ["pytest"] + +[[package]] +name = "cycler" +version = "0.11.0" +description = "Composable style cycles" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, + {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, +] [[package]] name = "fastapi" @@ -104,16 +230,20 @@ description = "FastAPI framework, high performance, easy to learn, fast to code, category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "fastapi-0.65.3-py3-none-any.whl", hash = "sha256:d3e3c0ac35110efb22ee3ed28201cf32f9d11a9a0e52d7dd676cad25f5219523"}, + {file = "fastapi-0.65.3.tar.gz", hash = "sha256:6ea2286e439c4ced7cce2b2862c25859601bf327a515c12dd6e431ef5d49d12f"}, +] [package.dependencies] pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" starlette = "0.14.2" [package.extras] -all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] -dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "graphene (>=2.1.8,<3.0.0)"] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "markdown-include (>=0.6.0,<0.7.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"] -test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.812)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.4.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] +all = ["aiofiles (>=0.5.0,<0.6.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "email_validator (>=1.1.1,<2.0.0)", "graphene (>=2.1.8,<3.0.0)", "itsdangerous (>=1.1.0,<2.0.0)", "jinja2 (>=2.11.2,<3.0.0)", "orjson (>=3.2.1,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "pyyaml (>=5.3.1,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "graphene (>=2.1.8,<3.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)"] +doc = ["markdown-include (>=0.6.0,<0.7.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "typer-cli (>=0.0.12,<0.0.13)"] +test = ["aiofiles (>=0.5.0,<0.6.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "black (==20.8b1)", "databases[sqlite] (>=0.3.2,<0.4.0)", "email_validator (>=1.1.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "flask (>=1.1.2,<2.0.0)", "httpx (>=0.14.0,<0.15.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.812)", "orjson (>=3.2.1,<4.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (==5.4.3)", "pytest-asyncio (>=0.14.0,<0.15.0)", "pytest-cov (==2.10.0)", "python-multipart (>=0.0.5,<0.0.6)", "requests (>=2.24.0,<3.0.0)", "sqlalchemy (>=1.3.18,<1.4.0)", "ujson (>=4.0.1,<5.0.0)"] [[package]] name = "flake8" @@ -122,30 +252,68 @@ description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, +] [package.dependencies] mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.7.0,<2.8.0" pyflakes = ">=2.3.0,<2.4.0" +[[package]] +name = "fonttools" +version = "4.39.3" +description = "Tools to manipulate font files" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.39.3-py3-none-any.whl", hash = "sha256:64c0c05c337f826183637570ac5ab49ee220eec66cf50248e8df527edfa95aeb"}, + {file = "fonttools-4.39.3.zip", hash = "sha256:9234b9f57b74e31b192c3fc32ef1a40750a8fbc1cd9837a7b7bfc4ca4a5c51d7"}, +] + +[package.extras] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] +graphite = ["lz4 (>=1.7.4.2)"] +interpolatable = ["munkres", "scipy"] +lxml = ["lxml (>=4.0,<5)"] +pathops = ["skia-pathops (>=0.5.0)"] +plot = ["matplotlib"] +repacker = ["uharfbuzz (>=0.23.0)"] +symfont = ["sympy"] +type1 = ["xattr"] +ufo = ["fs (>=2.2.0,<3)"] +unicode = ["unicodedata2 (>=15.0.0)"] +woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] + [[package]] name = "gitdb" -version = "4.0.9" +version = "4.0.10" description = "Git Object Database" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, + {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, +] [package.dependencies] smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.27" -description = "GitPython is a python library used to interact with Git repositories" +version = "3.1.31" +description = "GitPython is a Python library used to interact with Git repositories" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"}, + {file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"}, +] [package.dependencies] gitdb = ">=4.0.1,<5" @@ -157,6 +325,13 @@ description = "WSGI HTTP Server for UNIX" category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, + {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, +] + +[package.dependencies] +setuptools = ">=3.0" [package.extras] eventlet = ["eventlet (>=0.24.1)"] @@ -166,49 +341,331 @@ tornado = ["tornado (>=0.2)"] [[package]] name = "h11" -version = "0.13.0" +version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "hnswlib" +version = "0.6.2" +description = "hnswlib" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "hnswlib-0.6.2.tar.gz", hash = "sha256:b696d13f3678aa6795d054a0e8c33b3a2c8e14908432aaf4ced7ebef8290a10c"}, +] + +[package.dependencies] +numpy = "*" [[package]] name = "idna" version = "2.10" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] + +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] [[package]] name = "isort" -version = "5.10.1" +version = "5.12.0" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6.1,<4.0" +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] [package.extras] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -requirements_deprecated_finder = ["pipreqs", "pip-api"] -colors = ["colorama (>=0.4.3,<0.5.0)"] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "joblib" +version = "1.2.0" +description = "Lightweight pipelining with Python functions" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "joblib-1.2.0-py3-none-any.whl", hash = "sha256:091138ed78f800342968c523bdde947e7a305b8594b910a0fea2ab83c3c6d385"}, + {file = "joblib-1.2.0.tar.gz", hash = "sha256:e1cee4a79e4af22881164f218d4311f60074197fb707e082e803b61f6d137018"}, +] + +[[package]] +name = "kiwisolver" +version = "1.4.4" +description = "A fast implementation of the Cassowary constraint solver" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ea21f66820452a3f5d1655f8704a60d66ba1191359b96541eaf457710a5fc6"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bc9db8a3efb3e403e4ecc6cd9489ea2bac94244f80c78e27c31dcc00d2790ac2"}, + {file = "kiwisolver-1.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5b61785a9ce44e5a4b880272baa7cf6c8f48a5180c3e81c59553ba0cb0821ca"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2dbb44c3f7e6c4d3487b31037b1bdbf424d97687c1747ce4ff2895795c9bf69"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6295ecd49304dcf3bfbfa45d9a081c96509e95f4b9d0eb7ee4ec0530c4a96514"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bd472dbe5e136f96a4b18f295d159d7f26fd399136f5b17b08c4e5f498cd494"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf7d9fce9bcc4752ca4a1b80aabd38f6d19009ea5cbda0e0856983cf6d0023f5"}, + {file = "kiwisolver-1.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d6601aed50c74e0ef02f4204da1816147a6d3fbdc8b3872d263338a9052c51"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:877272cf6b4b7e94c9614f9b10140e198d2186363728ed0f701c6eee1baec1da"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:db608a6757adabb32f1cfe6066e39b3706d8c3aa69bbc353a5b61edad36a5cb4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5853eb494c71e267912275e5586fe281444eb5e722de4e131cddf9d442615626"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f0a1dbdb5ecbef0d34eb77e56fcb3e95bbd7e50835d9782a45df81cc46949750"}, + {file = "kiwisolver-1.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:283dffbf061a4ec60391d51e6155e372a1f7a4f5b15d59c8505339454f8989e4"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win32.whl", hash = "sha256:d06adcfa62a4431d404c31216f0f8ac97397d799cd53800e9d3efc2fbb3cf14e"}, + {file = "kiwisolver-1.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e7da3fec7408813a7cebc9e4ec55afed2d0fd65c4754bc376bf03498d4e92686"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:28bc5b299f48150b5f822ce68624e445040595a4ac3d59251703779836eceff9"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:81e38381b782cc7e1e46c4e14cd997ee6040768101aefc8fa3c24a4cc58e98f8"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2a66fdfb34e05b705620dd567f5a03f239a088d5a3f321e7b6ac3239d22aa286"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:872b8ca05c40d309ed13eb2e582cab0c5a05e81e987ab9c521bf05ad1d5cf5cb"}, + {file = "kiwisolver-1.4.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:70e7c2e7b750585569564e2e5ca9845acfaa5da56ac46df68414f29fea97be9f"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9f85003f5dfa867e86d53fac6f7e6f30c045673fa27b603c397753bebadc3008"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e307eb9bd99801f82789b44bb45e9f541961831c7311521b13a6c85afc09767"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1792d939ec70abe76f5054d3f36ed5656021dcad1322d1cc996d4e54165cef9"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cb459eea32a4e2cf18ba5fcece2dbdf496384413bc1bae15583f19e567f3b2"}, + {file = "kiwisolver-1.4.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36dafec3d6d6088d34e2de6b85f9d8e2324eb734162fba59d2ba9ed7a2043d5b"}, + {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, +] [[package]] name = "lazy-object-proxy" -version = "1.7.1" +version = "1.9.0" description = "A fast and thorough lazy object proxy." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, +] + +[[package]] +name = "lightfm" +version = "1.16" +description = "LightFM recommendation model" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "lightfm-1.16.tar.gz", hash = "sha256:41950fd8affde192c10b517148aa7f6d016ae2f75a2ec1187335f1c4d21876ac"}, +] + +[package.dependencies] +numpy = "*" +requests = "*" +scikit-learn = "*" +scipy = ">=0.17.0" + +[[package]] +name = "markdown-it-py" +version = "2.2.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "matplotlib" +version = "3.7.1" +description = "Python plotting package" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:95cbc13c1fc6844ab8812a525bbc237fa1470863ff3dace7352e910519e194b1"}, + {file = "matplotlib-3.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:08308bae9e91aca1ec6fd6dda66237eef9f6294ddb17f0d0b3c863169bf82353"}, + {file = "matplotlib-3.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:544764ba51900da4639c0f983b323d288f94f65f4024dc40ecb1542d74dc0500"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d94989191de3fcc4e002f93f7f1be5da476385dde410ddafbb70686acf00ea"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99bc9e65901bb9a7ce5e7bb24af03675cbd7c70b30ac670aa263240635999a4"}, + {file = "matplotlib-3.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb7d248c34a341cd4c31a06fd34d64306624c8cd8d0def7abb08792a5abfd556"}, + {file = "matplotlib-3.7.1-cp310-cp310-win32.whl", hash = "sha256:ce463ce590f3825b52e9fe5c19a3c6a69fd7675a39d589e8b5fbe772272b3a24"}, + {file = "matplotlib-3.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d7bc90727351fb841e4d8ae620d2d86d8ed92b50473cd2b42ce9186104ecbba"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:770a205966d641627fd5cf9d3cb4b6280a716522cd36b8b284a8eb1581310f61"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f67bfdb83a8232cb7a92b869f9355d677bce24485c460b19d01970b64b2ed476"}, + {file = "matplotlib-3.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2bf092f9210e105f414a043b92af583c98f50050559616930d884387d0772aba"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89768d84187f31717349c6bfadc0e0d8c321e8eb34522acec8a67b1236a66332"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83111e6388dec67822e2534e13b243cc644c7494a4bb60584edbff91585a83c6"}, + {file = "matplotlib-3.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a867bf73a7eb808ef2afbca03bcdb785dae09595fbe550e1bab0cd023eba3de0"}, + {file = "matplotlib-3.7.1-cp311-cp311-win32.whl", hash = "sha256:fbdeeb58c0cf0595efe89c05c224e0a502d1aa6a8696e68a73c3efc6bc354304"}, + {file = "matplotlib-3.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:c0bd19c72ae53e6ab979f0ac6a3fafceb02d2ecafa023c5cca47acd934d10be7"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:6eb88d87cb2c49af00d3bbc33a003f89fd9f78d318848da029383bfc08ecfbfb"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:cf0e4f727534b7b1457898c4f4ae838af1ef87c359b76dcd5330fa31893a3ac7"}, + {file = "matplotlib-3.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:46a561d23b91f30bccfd25429c3c706afe7d73a5cc64ef2dfaf2b2ac47c1a5dc"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8704726d33e9aa8a6d5215044b8d00804561971163563e6e6591f9dcf64340cc"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4cf327e98ecf08fcbb82685acaf1939d3338548620ab8dfa02828706402c34de"}, + {file = "matplotlib-3.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:617f14ae9d53292ece33f45cba8503494ee199a75b44de7717964f70637a36aa"}, + {file = "matplotlib-3.7.1-cp38-cp38-win32.whl", hash = "sha256:7c9a4b2da6fac77bcc41b1ea95fadb314e92508bf5493ceff058e727e7ecf5b0"}, + {file = "matplotlib-3.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:14645aad967684e92fc349493fa10c08a6da514b3d03a5931a1bac26e6792bd1"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:81a6b377ea444336538638d31fdb39af6be1a043ca5e343fe18d0f17e098770b"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:28506a03bd7f3fe59cd3cd4ceb2a8d8a2b1db41afede01f66c42561b9be7b4b7"}, + {file = "matplotlib-3.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8c587963b85ce41e0a8af53b9b2de8dddbf5ece4c34553f7bd9d066148dc719c"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8bf26ade3ff0f27668989d98c8435ce9327d24cffb7f07d24ef609e33d582439"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:def58098f96a05f90af7e92fd127d21a287068202aa43b2a93476170ebd99e87"}, + {file = "matplotlib-3.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f883a22a56a84dba3b588696a2b8a1ab0d2c3d41be53264115c71b0a942d8fdb"}, + {file = "matplotlib-3.7.1-cp39-cp39-win32.whl", hash = "sha256:4f99e1b234c30c1e9714610eb0c6d2f11809c9c78c984a613ae539ea2ad2eb4b"}, + {file = "matplotlib-3.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:3ba2af245e36990facf67fde840a760128ddd71210b2ab6406e640188d69d136"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3032884084f541163f295db8a6536e0abb0db464008fadca6c98aaf84ccf4717"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a2cb34336110e0ed8bb4f650e817eed61fa064acbefeb3591f1b33e3a84fd96"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b867e2f952ed592237a1828f027d332d8ee219ad722345b79a001f49df0936eb"}, + {file = "matplotlib-3.7.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:57bfb8c8ea253be947ccb2bc2d1bb3862c2bccc662ad1b4626e1f5e004557042"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:438196cdf5dc8d39b50a45cb6e3f6274edbcf2254f85fa9b895bf85851c3a613"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21e9cff1a58d42e74d01153360de92b326708fb205250150018a52c70f43c290"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75d4725d70b7c03e082bbb8a34639ede17f333d7247f56caceb3801cb6ff703d"}, + {file = "matplotlib-3.7.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:97cc368a7268141afb5690760921765ed34867ffb9655dd325ed207af85c7529"}, + {file = "matplotlib-3.7.1.tar.gz", hash = "sha256:7b73305f25eab4541bd7ee0b96d87e53ae9c9f1823be5659b806cd85786fe882"}, +] + +[package.dependencies] +contourpy = ">=1.0.1" +cycler = ">=0.10" +fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} +kiwisolver = ">=1.0.1" +numpy = ">=1.20" +packaging = ">=20.0" +pillow = ">=6.2.0" +pyparsing = ">=2.3.1" +python-dateutil = ">=2.7" [[package]] name = "mccabe" @@ -217,6 +674,22 @@ description = "McCabe checker, plugin for flake8" category = "dev" optional = false python-versions = "*" +files = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] [[package]] name = "mypy" @@ -225,6 +698,30 @@ description = "Optional static typing for Python" category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, + {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, + {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, + {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, + {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, + {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, + {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, + {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, + {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, + {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, + {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, + {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, + {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, + {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, + {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, + {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, + {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, + {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, + {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, + {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, + {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, + {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, +] [package.dependencies] mypy-extensions = ">=0.4.3,<0.5.0" @@ -236,38 +733,340 @@ dmypy = ["psutil (>=4.0)"] [[package]] name = "mypy-extensions" -version = "0.4.3" +version = "0.4.4" description = "Experimental type system extensions for programs checked with the mypy typechecker." category = "dev" optional = false +python-versions = ">=2.7" +files = [ + {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, +] + +[[package]] +name = "nmslib" +version = "2.1.1" +description = "Non-Metric Space Library (NMSLIB)" +category = "main" +optional = false python-versions = "*" +files = [ + {file = "nmslib-2.1.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:495ace1146bb1e89ef585a490fa65de19a87fa0d5bb029f3156402a431fc3558"}, + {file = "nmslib-2.1.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:c0ae48f01e63e70dc1e45b28cb1b1478ca09588a21127f1d4473afb22ff3bbbc"}, + {file = "nmslib-2.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:97b3f1af2c163369a043449dec7f05d681aa23c00f9d77bee09fd8f31ba41815"}, + {file = "nmslib-2.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:2061e94f980666bffff4f2173bf2b58a763062dee9cedab11ec46001e576c6b5"}, + {file = "nmslib-2.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e8b30bab631258b36e751d217180cdba0d2f942d779d4444cbc57a5173ee19ad"}, + {file = "nmslib-2.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:191a23dea20c0ad855e78df41a90273020f76d878c52aa1a80557ca83cf0f392"}, + {file = "nmslib-2.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:eae9a1d640a9e4c52c43272620f54f367f03ce25d8b07218fd90dfc204d5c49d"}, + {file = "nmslib-2.1.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:c22ffc3376d41b4b4e63d89ec5db2cf2e2051881827d658c94632800bed87e5b"}, + {file = "nmslib-2.1.1-cp35-cp35m-macosx_10_15_x86_64.whl", hash = "sha256:a1ff4aeb143747106994a68dfcc29b097fc4562f632f2709f8f467b93d7181e2"}, + {file = "nmslib-2.1.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:5ce529d4f9b572135cdc5a18f04d3cbe258f1f440f8a06ab7209428a3b4c30d6"}, + {file = "nmslib-2.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:00f5b37305837fd5656ab4a65c4f0c9bc8e44cc9cf77cc9222f4153a72f0c727"}, + {file = "nmslib-2.1.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:08cb77c3fc2a346edc3407bd985b63a05bd0ec0d6d81ff2c6f7d6eb6bfbdbc1d"}, + {file = "nmslib-2.1.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:7dcc203450c9d95a551e9564646b9d0179edbde0874373ad63739b7e25a9980c"}, + {file = "nmslib-2.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:db4b817f0896e0bbc836722c67b51bc237dc79477d2d383ced344d755705d409"}, + {file = "nmslib-2.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1b120f7766a52c4016c6b8889b34d97eddbb60a16fd39eb5a81e73ca101baf3e"}, + {file = "nmslib-2.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e5cd5d492bf7290adacac797ee092c863d230f3797d0db6a120a1a05c665be2"}, + {file = "nmslib-2.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0a80e574c3e6bfb724dbb77cbaec53cf9f4366382c2c0a9a2f5148c69b646afe"}, + {file = "nmslib-2.1.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:7b0d916f219b4044cd31e8f6bda92a7f23d68cb3f34f1c7da81e5852702d6ce0"}, + {file = "nmslib-2.1.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:a2129a7519ab73d3544ab6fa2f6c6c4c54644bf3d1c60d6929dcaac49c9307e6"}, + {file = "nmslib-2.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0f5c1c7e3c398a4ed7eaed65cd91776022b2ca4b8f15e504fa22c89da9333257"}, + {file = "nmslib-2.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c5a644d32ad3b2e24635a839d17d9d53d01a47ca55d1ba0d051f51f49df7e98a"}, + {file = "nmslib-2.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:fc75c7e128fd19c21c8cd73a1681a32d902bf5203699321cdfff8cf98607c82d"}, + {file = "nmslib-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:43118bb64f06e62662a8c7f1fe54ae699a078b438e3f8cb75db547731a5e73f6"}, + {file = "nmslib-2.1.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:1b87f7a5813cabedef47b289337ab1eefa4e62485a05884c2dbe7ba1d9c327a4"}, + {file = "nmslib-2.1.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:ca3d4eb91232078fb96eaa938a4dcbcfb3da48a7800350e1ded70d8d3e6e65a5"}, + {file = "nmslib-2.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b6ebb53999672abf7811ef35e2ec3d76b2a845fd5dbaa5482e2cc445481d87cf"}, + {file = "nmslib-2.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:26062f5f172c35038c46feabb5c60c104808c167db0c3dfab19d11eb99f18b85"}, + {file = "nmslib-2.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:ed63fda2a5572391a0a540689f00d390040b9255e08012ea43344958cea92f36"}, + {file = "nmslib-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:6a3aa123d85c188fc64cb8957e5cdded9c8c57bea2c6a5866fe06c729e6e6e85"}, + {file = "nmslib-2.1.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:3fe5bbf304fdde50f3a55329efcf26d4ba4eb1181885877a1baab1df900dd671"}, + {file = "nmslib-2.1.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:7b5a561a71dce6d56b32ea89a591dbe477b4173fbc3e9a1153d534bc248ae7b5"}, + {file = "nmslib-2.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:82680077a06a817df95ea716260231245cf077a4c08b0f39148d4a58d2eb58f2"}, + {file = "nmslib-2.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be54644e1a1a6e2539e565fe24415997f18656a5260276c40266cacb70e2a9ab"}, + {file = "nmslib-2.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:3681e3e9160fa9661192c19202402e22151e016cf365621609ae5ca4a4677d21"}, + {file = "nmslib-2.1.1.tar.gz", hash = "sha256:971294a7766f91c697d63c88c8adda4797a944346cc535a217393f4167a19c20"}, +] + +[package.dependencies] +numpy = {version = ">=1.10.0", markers = "python_version >= \"3.5\""} +psutil = "*" +pybind11 = "<2.6.2" + +[[package]] +name = "numpy" +version = "1.24.2" +description = "Fundamental package for array computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d"}, + {file = "numpy-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5"}, + {file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253"}, + {file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978"}, + {file = "numpy-1.24.2-cp310-cp310-win32.whl", hash = "sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9"}, + {file = "numpy-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0"}, + {file = "numpy-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a"}, + {file = "numpy-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0"}, + {file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281"}, + {file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910"}, + {file = "numpy-1.24.2-cp311-cp311-win32.whl", hash = "sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95"}, + {file = "numpy-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04"}, + {file = "numpy-1.24.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2"}, + {file = "numpy-1.24.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5"}, + {file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a"}, + {file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96"}, + {file = "numpy-1.24.2-cp38-cp38-win32.whl", hash = "sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d"}, + {file = "numpy-1.24.2-cp38-cp38-win_amd64.whl", hash = "sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756"}, + {file = "numpy-1.24.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a"}, + {file = "numpy-1.24.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f"}, + {file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb"}, + {file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780"}, + {file = "numpy-1.24.2-cp39-cp39-win32.whl", hash = "sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468"}, + {file = "numpy-1.24.2-cp39-cp39-win_amd64.whl", hash = "sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5"}, + {file = "numpy-1.24.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d"}, + {file = "numpy-1.24.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa"}, + {file = "numpy-1.24.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f"}, + {file = "numpy-1.24.2.tar.gz", hash = "sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22"}, +] [[package]] name = "orjson" -version = "3.7.7" +version = "3.8.10" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">= 3.7" +files = [ + {file = "orjson-3.8.10-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:4dfe0651e26492d5d929bbf4322de9afbd1c51ac2e3947a7f78492b20359711d"}, + {file = "orjson-3.8.10-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:bc30de5c7b3a402eb59cc0656b8ee53ca36322fc52ab67739c92635174f88336"}, + {file = "orjson-3.8.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c08b426fae7b9577b528f99af0f7e0ff3ce46858dd9a7d1bf86d30f18df89a4c"}, + {file = "orjson-3.8.10-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bce970f293825e008dbf739268dfa41dfe583aa2a1b5ef4efe53a0e92e9671ea"}, + {file = "orjson-3.8.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b23fb0264bbdd7218aa685cb6fc71f0dcecf34182f0a8596a3a0dff010c06f9"}, + {file = "orjson-3.8.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0826ad2dc1cea1547edff14ce580374f0061d853cbac088c71162dbfe2e52205"}, + {file = "orjson-3.8.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7bce6e61cea6426309259b04c6ee2295b3f823ea51a033749459fe2dd0423b2"}, + {file = "orjson-3.8.10-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0b470d31244a6f647e5402aac7d2abaf7bb4f52379acf67722a09d35a45c9417"}, + {file = "orjson-3.8.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:48824649019a25d3e52f6454435cf19fe1eb3d05ee697e65d257f58ae3aa94d9"}, + {file = "orjson-3.8.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:faee89e885796a9cc493c930013fa5cfcec9bfaee431ddf00f0fbfb57166a8b3"}, + {file = "orjson-3.8.10-cp310-none-win_amd64.whl", hash = "sha256:3cfe32b1227fe029a5ad989fbec0b453a34e5e6d9a977723f7c3046d062d3537"}, + {file = "orjson-3.8.10-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:2073b62822738d6740bd2492f6035af5c2fd34aa198322b803dc0e70559a17b7"}, + {file = "orjson-3.8.10-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:b2c4faf20b6bb5a2d7ac0c16f58eb1a3800abcef188c011296d1dc2bb2224d48"}, + {file = "orjson-3.8.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c1825997232a324911d11c75d91e1e0338c7b723c149cf53a5fc24496c048a4"}, + {file = "orjson-3.8.10-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7e85d4682f3ed7321d36846cad0503e944ea9579ef435d4c162e1b73ead8ac9"}, + {file = "orjson-3.8.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8cdaacecb92997916603ab232bb096d0fa9e56b418ca956b9754187d65ca06"}, + {file = "orjson-3.8.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ddabc5e44702d13137949adee3c60b7091e73a664f6e07c7b428eebb2dea7bbf"}, + {file = "orjson-3.8.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27bb26e171e9cfdbec39c7ca4739b6bef8bd06c293d56d92d5e3a3fc017df17d"}, + {file = "orjson-3.8.10-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1810e5446fe68d61732e9743592da0ec807e63972eef076d09e02878c2f5958e"}, + {file = "orjson-3.8.10-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61e2e51cefe7ef90c4fbbc9fd38ecc091575a3ea7751d56fad95cbebeae2a054"}, + {file = "orjson-3.8.10-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f3e9ac9483c2b4cd794e760316966b7bd1e6afb52b0218f068a4e80c9b2db4f6"}, + {file = "orjson-3.8.10-cp311-none-win_amd64.whl", hash = "sha256:26aee557cf8c93b2a971b5a4a8e3cca19780573531493ce6573aa1002f5c4378"}, + {file = "orjson-3.8.10-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:11ae68f995a50724032af297c92f20bcde31005e0bf3653b12bff9356394615b"}, + {file = "orjson-3.8.10-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:35d879b46b8029e1e01e9f6067928b470a4efa1ca749b6d053232b873c2dcf66"}, + {file = "orjson-3.8.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:345e41abd1d9e3ecfb554e1e75ff818cf42e268bd06ad25a96c34e00f73a327e"}, + {file = "orjson-3.8.10-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:45a5afc9cda6b8aac066dd50d8194432fbc33e71f7164f95402999b725232d78"}, + {file = "orjson-3.8.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad632dc330a7b39da42530c8d146f76f727d476c01b719dc6743c2b5701aaf6b"}, + {file = "orjson-3.8.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bf2556ba99292c4dc550560384dd22e88b5cdbe6d98fb4e202e902b5775cf9f"}, + {file = "orjson-3.8.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b88afd662190f19c3bb5036a903589f88b1d2c2608fbb97281ce000db6b08897"}, + {file = "orjson-3.8.10-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:abce8d319aae800fd2d774db1106f926dee0e8a5ca85998fd76391fcb58ef94f"}, + {file = "orjson-3.8.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e999abca892accada083f7079612307d94dd14cc105a699588a324f843216509"}, + {file = "orjson-3.8.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3fdee68c4bb3c5d6f89ed4560f1384b5d6260e48fbf868bae1a245a3c693d4d"}, + {file = "orjson-3.8.10-cp37-none-win_amd64.whl", hash = "sha256:e5d7f82506212e047b184c06e4bcd48c1483e101969013623cebcf51cf12cad9"}, + {file = "orjson-3.8.10-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:d953e6c2087dcd990e794f8405011369ee11cf13e9aaae3172ee762ee63947f2"}, + {file = "orjson-3.8.10-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:81aa3f321d201bff0bd0f4014ea44e51d58a9a02d8f2b0eeab2cee22611be8e1"}, + {file = "orjson-3.8.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d27b6182f75896dd8c10ea0f78b9265a3454be72d00632b97f84d7031900dd4"}, + {file = "orjson-3.8.10-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1486600bc1dd1db26c588dd482689edba3d72d301accbe4301db4b2b28bd7aa4"}, + {file = "orjson-3.8.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344ea91c556a2ce6423dc13401b83ab0392aa697a97fa4142c2c63a6fd0bbfef"}, + {file = "orjson-3.8.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:979f231e3bad1c835627eef1a30db12a8af58bfb475a6758868ea7e81897211f"}, + {file = "orjson-3.8.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa3a26dcf0f5f2912a8ce8e87273e68b2a9526854d19fd09ea671b154418e88"}, + {file = "orjson-3.8.10-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:b6e79d8864794635974b18821b49a7f27859d17b93413d4603efadf2e92da7a5"}, + {file = "orjson-3.8.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ce49999bcbbc14791c61844bc8a69af44f5205d219be540e074660038adae6bf"}, + {file = "orjson-3.8.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c2ef690335b24f9272dbf6639353c1ffc3f196623a92b851063e28e9515cf7dd"}, + {file = "orjson-3.8.10-cp38-none-win_amd64.whl", hash = "sha256:5a0b1f4e4fa75e26f814161196e365fc0e1a16e3c07428154505b680a17df02f"}, + {file = "orjson-3.8.10-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:af7601a78b99f0515af2f8ab12c955c0072ffcc1e437fb2556f4465783a4d813"}, + {file = "orjson-3.8.10-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6bbd7b3a3e2030b03c68c4d4b19a2ef5b89081cbb43c05fe2010767ef5e408db"}, + {file = "orjson-3.8.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4355c9aedfefe60904e8bd7901315ebbc8bb828f665e4c9bc94b1432e67cb6f7"}, + {file = "orjson-3.8.10-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b7b0ba074375e25c1594e770e2215941e2017c3cd121889150737fa1123e8bfe"}, + {file = "orjson-3.8.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34b6901c110c06ab9e8d7d0496db4bc9a0c162ca8d77f67539d22cb39e0a1ef4"}, + {file = "orjson-3.8.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb62ec16a1c26ad9487727b529103cb6a94a1d4969d5b32dd0eab5c3f4f5a6f2"}, + {file = "orjson-3.8.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595e1e7d04aaaa3d41113e4eb9f765ab642173c4001182684ae9ddc621bb11c8"}, + {file = "orjson-3.8.10-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:64ffd92328473a2f9af059410bd10c703206a4bbc7b70abb1bedcd8761e39eb8"}, + {file = "orjson-3.8.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b1f648ec89c6a426098868460c0ef8c86b457ce1378d7569ff4acb6c0c454048"}, + {file = "orjson-3.8.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6a286ad379972e4f46579e772f0477e6b505f1823aabcd64ef097dbb4549e1a4"}, + {file = "orjson-3.8.10-cp39-none-win_amd64.whl", hash = "sha256:d2874cee6856d7c386b596e50bc517d1973d73dc40b2bd6abec057b5e7c76b2f"}, + {file = "orjson-3.8.10.tar.gz", hash = "sha256:dcf6adb4471b69875034afab51a14b64f1026bc968175a2bb02c5f6b358bd413"}, +] [[package]] name = "packaging" -version = "21.3" +version = "23.0" description = "Core utilities for Python packages" -category = "dev" +category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, + {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, +] + +[[package]] +name = "pandas" +version = "2.0.0" +description = "Powerful data structures for data analysis, time series, and statistics" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pandas-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bbb2c5e94d6aa4e632646a3bacd05c2a871c3aa3e85c9bec9be99cb1267279f2"}, + {file = "pandas-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b5337c87c4e963f97becb1217965b6b75c6fe5f54c4cf09b9a5ac52fc0bd03d3"}, + {file = "pandas-2.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ded51f7e3dd9b4f8b87f2ceb7bd1a8df2491f7ee72f7074c6927a512607199e"}, + {file = "pandas-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c858de9e9fc422d25e67e1592a6e6135d7bcf9a19fcaf4d0831a0be496bf21"}, + {file = "pandas-2.0.0-cp310-cp310-win32.whl", hash = "sha256:2d1d138848dd71b37e3cbe7cd952ff84e2ab04d8988972166e18567dcc811245"}, + {file = "pandas-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:d08e41d96bc4de6f500afe80936c68fce6099d5a434e2af7c7fd8e7c72a3265d"}, + {file = "pandas-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24472cfc7ced511ac90608728b88312be56edc8f19b9ed885a7d2e47ffaf69c0"}, + {file = "pandas-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ffb14f50c74ee541610668137830bb93e9dfa319b1bef2cedf2814cd5ac9c70"}, + {file = "pandas-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c24c7d12d033a372a9daf9ff2c80f8b0af6f98d14664dbb0a4f6a029094928a7"}, + {file = "pandas-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8318de0f886e4dcb8f9f36e45a3d6a6c3d1cfdc508354da85e739090f0222991"}, + {file = "pandas-2.0.0-cp311-cp311-win32.whl", hash = "sha256:57c34b79c13249505e850d0377b722961b99140f81dafbe6f19ef10239f6284a"}, + {file = "pandas-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f987ec26e96a8490909bc5d98c514147236e49830cba7df8690f6087c12bbae"}, + {file = "pandas-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b3ba8f5dd470d8bfbc4259829589f4a32881151c49e36384d9eb982b35a12020"}, + {file = "pandas-2.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fcd471c9d9f60926ab2f15c6c29164112f458acb42280365fbefa542d0c2fc74"}, + {file = "pandas-2.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9253edfd015520ce77a9343eb7097429479c039cd3ebe81d7810ea11b4b24695"}, + {file = "pandas-2.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977326039bd1ded620001a1889e2ed4798460a6bc5a24fbaebb5f07a41c32a55"}, + {file = "pandas-2.0.0-cp38-cp38-win32.whl", hash = "sha256:78425ca12314b23356c28b16765639db10ebb7d8983f705d6759ff7fe41357fa"}, + {file = "pandas-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:d93b7fcfd9f3328072b250d6d001dcfeec5d3bb66c1b9c8941e109a46c0c01a8"}, + {file = "pandas-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:425705cee8be54db2504e8dd2a730684790b15e5904b750c367611ede49098ab"}, + {file = "pandas-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a4f789b7c012a608c08cda4ff0872fd979cb18907a37982abe884e6f529b8793"}, + {file = "pandas-2.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bb9d840bf15656805f6a3d87eea9dcb7efdf1314a82adcf7f00b820427c5570"}, + {file = "pandas-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0778ab54c8f399d83d98ffb674d11ec716449956bc6f6821891ab835848687f2"}, + {file = "pandas-2.0.0-cp39-cp39-win32.whl", hash = "sha256:70db5c278bbec0306d32bf78751ff56b9594c05a5098386f6c8a563659124f91"}, + {file = "pandas-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f3320bb55f34af4193020158ef8118ee0fb9aec7cc47d2084dbfdd868a0a24f"}, + {file = "pandas-2.0.0.tar.gz", hash = "sha256:cda9789e61b44463c1c4fe17ef755de77bcd13b09ba31c940d20f193d63a5dc8"}, +] [package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +numpy = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, + {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, + {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.1" + +[package.extras] +all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] +aws = ["s3fs (>=2021.08.0)"] +clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] +compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] +computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] +feather = ["pyarrow (>=7.0.0)"] +fss = ["fsspec (>=2021.07.0)"] +gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] +hdf5 = ["tables (>=3.6.1)"] +html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] +mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] +parquet = ["pyarrow (>=7.0.0)"] +performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] +plot = ["matplotlib (>=3.6.1)"] +postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] +spss = ["pyreadstat (>=1.1.2)"] +sql-other = ["SQLAlchemy (>=1.4.16)"] +test = ["hypothesis (>=6.34.2)", "pytest (>=7.0.0)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.6.3)"] [[package]] name = "pbr" -version = "5.9.0" +version = "5.11.1" description = "Python Build Reasonableness" category = "dev" optional = false python-versions = ">=2.6" +files = [ + {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, + {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, +] + +[[package]] +name = "pillow" +version = "9.5.0" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, + {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, + {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, + {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, + {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, + {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, + {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, + {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, + {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, + {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, + {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, + {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, + {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, + {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, + {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, + {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, + {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] [[package]] name = "pluggy" @@ -276,11 +1075,42 @@ description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "psutil" +version = "5.9.4" +description = "Cross-platform lib for process and system monitoring in Python." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"}, + {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, + {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"}, + {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, + {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"}, + {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"}, + {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"}, + {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"}, + {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, + {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"}, + {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "py" version = "1.11.0" @@ -288,6 +1118,25 @@ description = "library with cross-python path, ini-parsing, io, code, log facili category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] + +[[package]] +name = "pybind11" +version = "2.6.1" +description = "Seamless operability between C++11 and Python" +category = "main" +optional = false +python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" +files = [ + {file = "pybind11-2.6.1-py2.py3-none-any.whl", hash = "sha256:c3691da74b670a4850dec30c1145a0dad53a50eeca78b7e7cdc855b5c98fd32d"}, + {file = "pybind11-2.6.1.tar.gz", hash = "sha256:ab7e60a520fe6ae25eca939191bb2ac416cd58478ce754740238a8bf1af18934"}, +] + +[package.extras] +global = ["pybind11-global (==2.6.1)"] [[package]] name = "pycodestyle" @@ -296,17 +1145,59 @@ description = "Python style guide checker" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] [[package]] name = "pydantic" -version = "1.9.1" +version = "1.10.7" description = "Data validation and settings management using python type hints" category = "main" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7" +files = [ + {file = "pydantic-1.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e79e999e539872e903767c417c897e729e015872040e56b96e67968c3b918b2d"}, + {file = "pydantic-1.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:01aea3a42c13f2602b7ecbbea484a98169fb568ebd9e247593ea05f01b884b2e"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:516f1ed9bc2406a0467dd777afc636c7091d71f214d5e413d64fef45174cfc7a"}, + {file = "pydantic-1.10.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae150a63564929c675d7f2303008d88426a0add46efd76c3fc797cd71cb1b46f"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ecbbc51391248116c0a055899e6c3e7ffbb11fb5e2a4cd6f2d0b93272118a209"}, + {file = "pydantic-1.10.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f4a2b50e2b03d5776e7f21af73e2070e1b5c0d0df255a827e7c632962f8315af"}, + {file = "pydantic-1.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:a7cd2251439988b413cb0a985c4ed82b6c6aac382dbaff53ae03c4b23a70e80a"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:68792151e174a4aa9e9fc1b4e653e65a354a2fa0fed169f7b3d09902ad2cb6f1"}, + {file = "pydantic-1.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe2507b8ef209da71b6fb5f4e597b50c5a34b78d7e857c4f8f3115effaef5fe"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a86d8c8db68086f1e30a530f7d5f83eb0685e632e411dbbcf2d5c0150e8dcd"}, + {file = "pydantic-1.10.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75ae19d2a3dbb146b6f324031c24f8a3f52ff5d6a9f22f0683694b3afcb16fb"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:464855a7ff7f2cc2cf537ecc421291b9132aa9c79aef44e917ad711b4a93163b"}, + {file = "pydantic-1.10.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:193924c563fae6ddcb71d3f06fa153866423ac1b793a47936656e806b64e24ca"}, + {file = "pydantic-1.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:b4a849d10f211389502059c33332e91327bc154acc1845f375a99eca3afa802d"}, + {file = "pydantic-1.10.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cc1dde4e50a5fc1336ee0581c1612215bc64ed6d28d2c7c6f25d2fe3e7c3e918"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0cfe895a504c060e5d36b287ee696e2fdad02d89e0d895f83037245218a87fe"}, + {file = "pydantic-1.10.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670bb4683ad1e48b0ecb06f0cfe2178dcf74ff27921cdf1606e527d2617a81ee"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:950ce33857841f9a337ce07ddf46bc84e1c4946d2a3bba18f8280297157a3fd1"}, + {file = "pydantic-1.10.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c15582f9055fbc1bfe50266a19771bbbef33dd28c45e78afbe1996fd70966c2a"}, + {file = "pydantic-1.10.7-cp37-cp37m-win_amd64.whl", hash = "sha256:82dffb306dd20bd5268fd6379bc4bfe75242a9c2b79fec58e1041fbbdb1f7914"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c7f51861d73e8b9ddcb9916ae7ac39fb52761d9ea0df41128e81e2ba42886cd"}, + {file = "pydantic-1.10.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6434b49c0b03a51021ade5c4daa7d70c98f7a79e95b551201fff682fc1661245"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64d34ab766fa056df49013bb6e79921a0265204c071984e75a09cbceacbbdd5d"}, + {file = "pydantic-1.10.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:701daea9ffe9d26f97b52f1d157e0d4121644f0fcf80b443248434958fd03dc3"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf135c46099ff3f919d2150a948ce94b9ce545598ef2c6c7bf55dca98a304b52"}, + {file = "pydantic-1.10.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0f85904f73161817b80781cc150f8b906d521fa11e3cdabae19a581c3606209"}, + {file = "pydantic-1.10.7-cp38-cp38-win_amd64.whl", hash = "sha256:9f6f0fd68d73257ad6685419478c5aece46432f4bdd8d32c7345f1986496171e"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c230c0d8a322276d6e7b88c3f7ce885f9ed16e0910354510e0bae84d54991143"}, + {file = "pydantic-1.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:976cae77ba6a49d80f461fd8bba183ff7ba79f44aa5cfa82f1346b5626542f8e"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d45fc99d64af9aaf7e308054a0067fdcd87ffe974f2442312372dfa66e1001d"}, + {file = "pydantic-1.10.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2a5ebb48958754d386195fe9e9c5106f11275867051bf017a8059410e9abf1f"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:abfb7d4a7cd5cc4e1d1887c43503a7c5dd608eadf8bc615413fc498d3e4645cd"}, + {file = "pydantic-1.10.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:80b1fab4deb08a8292d15e43a6edccdffa5377a36a4597bb545b93e79c5ff0a5"}, + {file = "pydantic-1.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:d71e69699498b020ea198468e2480a2f1e7433e32a3a99760058c6520e2bea7e"}, + {file = "pydantic-1.10.7-py3-none-any.whl", hash = "sha256:0cd181f1d0b1d00e2b705f1bf1ac7799a2d938cce3376b8007df62b29be3c2c6"}, + {file = "pydantic-1.10.7.tar.gz", hash = "sha256:cfc83c0678b6ba51b0532bea66860617c4cd4251ecf76e9846fa5a9f3454e97e"}, +] [package.dependencies] -typing-extensions = ">=3.7.4.3" +typing-extensions = ">=4.2.0" [package.extras] dotenv = ["python-dotenv (>=0.10.4)"] @@ -319,6 +1210,25 @@ description = "passive checker of Python programs" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] + +[[package]] +name = "pygments" +version = "2.15.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.0-py3-none-any.whl", hash = "sha256:77a3299119af881904cd5ecd1ac6a66214b6e9bed1f2db16993b54adede64094"}, + {file = "Pygments-2.15.0.tar.gz", hash = "sha256:f7e36cffc4c517fbc252861b9a6e4644ca0e5abadf9a113c72d1358ad09b9500"}, +] + +[package.extras] +plugins = ["importlib-metadata"] [[package]] name = "pylint" @@ -327,6 +1237,10 @@ description = "python code static checker" category = "dev" optional = false python-versions = "~=3.6" +files = [ + {file = "pylint-2.8.3-py3-none-any.whl", hash = "sha256:792b38ff30903884e4a9eab814ee3523731abd3c463f3ba48d7b627e87013484"}, + {file = "pylint-2.8.3.tar.gz", hash = "sha256:0a049c5d47b629d9070c3932d13bff482b12119b6a241a93bc460b0be16953c8"}, +] [package.dependencies] astroid = "2.5.6" @@ -339,12 +1253,16 @@ toml = ">=0.7.1" name = "pyparsing" version = "3.0.9" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "dev" +category = "main" optional = false python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] [package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" @@ -353,6 +1271,10 @@ description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, + {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, +] [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} @@ -368,20 +1290,93 @@ toml = "*" testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "dev" +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" optional = false -python-versions = ">=3.6" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] [[package]] name = "requests" version = "2.25.1" description = "Python HTTP for Humans." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, +] [package.dependencies] certifi = ">=2017.4.17" @@ -390,9 +1385,204 @@ idna = ">=2.5,<3" urllib3 = ">=1.21.1,<1.27" [package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +security = ["cryptography (>=1.3.4)", "pyOpenSSL (>=0.14)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +[[package]] +name = "rich" +version = "13.3.3" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "dev" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.3.3-py3-none-any.whl", hash = "sha256:540c7d6d26a1178e8e8b37e9ba44573a3cd1464ff6348b99ee7061b95d1c6333"}, + {file = "rich-13.3.3.tar.gz", hash = "sha256:dc84400a9d842b3a9c5ff74addd8eb798d155f36c1c91303888e0a66850d2a15"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0,<3.0.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "scikit-learn" +version = "1.2.2" +description = "A set of python modules for machine learning and data mining" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scikit-learn-1.2.2.tar.gz", hash = "sha256:8429aea30ec24e7a8c7ed8a3fa6213adf3814a6efbea09e16e0a0c71e1a1a3d7"}, + {file = "scikit_learn-1.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99cc01184e347de485bf253d19fcb3b1a3fb0ee4cea5ee3c43ec0cc429b6d29f"}, + {file = "scikit_learn-1.2.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e6e574db9914afcb4e11ade84fab084536a895ca60aadea3041e85b8ac963edb"}, + {file = "scikit_learn-1.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fe83b676f407f00afa388dd1fdd49e5c6612e551ed84f3b1b182858f09e987d"}, + {file = "scikit_learn-1.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2642baa0ad1e8f8188917423dd73994bf25429f8893ddbe115be3ca3183584"}, + {file = "scikit_learn-1.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ad66c3848c0a1ec13464b2a95d0a484fd5b02ce74268eaa7e0c697b904f31d6c"}, + {file = "scikit_learn-1.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfeaf8be72117eb61a164ea6fc8afb6dfe08c6f90365bde2dc16456e4bc8e45f"}, + {file = "scikit_learn-1.2.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:fe0aa1a7029ed3e1dcbf4a5bc675aa3b1bc468d9012ecf6c6f081251ca47f590"}, + {file = "scikit_learn-1.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:065e9673e24e0dc5113e2dd2b4ca30c9d8aa2fa90f4c0597241c93b63130d233"}, + {file = "scikit_learn-1.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf036ea7ef66115e0d49655f16febfa547886deba20149555a41d28f56fd6d3c"}, + {file = "scikit_learn-1.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:8b0670d4224a3c2d596fd572fb4fa673b2a0ccfb07152688ebd2ea0b8c61025c"}, + {file = "scikit_learn-1.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9c710ff9f9936ba8a3b74a455ccf0dcf59b230caa1e9ba0223773c490cab1e51"}, + {file = "scikit_learn-1.2.2-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:2dd3ffd3950e3d6c0c0ef9033a9b9b32d910c61bd06cb8206303fb4514b88a49"}, + {file = "scikit_learn-1.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44b47a305190c28dd8dd73fc9445f802b6ea716669cfc22ab1eb97b335d238b1"}, + {file = "scikit_learn-1.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:953236889928d104c2ef14027539f5f2609a47ebf716b8cbe4437e85dce42744"}, + {file = "scikit_learn-1.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:7f69313884e8eb311460cc2f28676d5e400bd929841a2c8eb8742ae78ebf7c20"}, + {file = "scikit_learn-1.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8156db41e1c39c69aa2d8599ab7577af53e9e5e7a57b0504e116cc73c39138dd"}, + {file = "scikit_learn-1.2.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fe175ee1dab589d2e1033657c5b6bec92a8a3b69103e3dd361b58014729975c3"}, + {file = "scikit_learn-1.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d5312d9674bed14f73773d2acf15a3272639b981e60b72c9b190a0cffed5bad"}, + {file = "scikit_learn-1.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea061bf0283bf9a9f36ea3c5d3231ba2176221bbd430abd2603b1c3b2ed85c89"}, + {file = "scikit_learn-1.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:6477eed40dbce190f9f9e9d0d37e020815825b300121307942ec2110302b66a3"}, +] + +[package.dependencies] +joblib = ">=1.1.1" +numpy = ">=1.17.3" +scipy = ">=1.3.2" +threadpoolctl = ">=2.0.0" + +[package.extras] +benchmark = ["matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "pandas (>=1.0.5)"] +docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.1.3)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "plotly (>=5.10.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)", "sphinx (>=4.0.1)", "sphinx-gallery (>=0.7.0)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] +examples = ["matplotlib (>=3.1.3)", "pandas (>=1.0.5)", "plotly (>=5.10.0)", "pooch (>=1.6.0)", "scikit-image (>=0.16.2)", "seaborn (>=0.9.0)"] +tests = ["black (>=22.3.0)", "flake8 (>=3.8.2)", "matplotlib (>=3.1.3)", "mypy (>=0.961)", "numpydoc (>=1.2.0)", "pandas (>=1.0.5)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pytest (>=5.3.1)", "pytest-cov (>=2.9.0)", "scikit-image (>=0.16.2)"] + +[[package]] +name = "scipy" +version = "1.9.3" +description = "Fundamental algorithms for scientific computing in Python" +category = "main" +optional = false +python-versions = ">=3.8" +files = [ + {file = "scipy-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1884b66a54887e21addf9c16fb588720a8309a57b2e258ae1c7986d4444d3bc0"}, + {file = "scipy-1.9.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:83b89e9586c62e787f5012e8475fbb12185bafb996a03257e9675cd73d3736dd"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a72d885fa44247f92743fc20732ae55564ff2a519e8302fb7e18717c5355a8b"}, + {file = "scipy-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01e1dd7b15bd2449c8bfc6b7cc67d630700ed655654f0dfcf121600bad205c9"}, + {file = "scipy-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:68239b6aa6f9c593da8be1509a05cb7f9efe98b80f43a5861cd24c7557e98523"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b41bc822679ad1c9a5f023bc93f6d0543129ca0f37c1ce294dd9d386f0a21096"}, + {file = "scipy-1.9.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:90453d2b93ea82a9f434e4e1cba043e779ff67b92f7a0e85d05d286a3625df3c"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c06e62a390a9167da60bedd4575a14c1f58ca9dfde59830fc42e5197283dab"}, + {file = "scipy-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abaf921531b5aeaafced90157db505e10345e45038c39e5d9b6c7922d68085cb"}, + {file = "scipy-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:06d2e1b4c491dc7d8eacea139a1b0b295f74e1a1a0f704c375028f8320d16e31"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a04cd7d0d3eff6ea4719371cbc44df31411862b9646db617c99718ff68d4840"}, + {file = "scipy-1.9.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:545c83ffb518094d8c9d83cce216c0c32f8c04aaf28b92cc8283eda0685162d5"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d54222d7a3ba6022fdf5773931b5d7c56efe41ede7f7128c7b1637700409108"}, + {file = "scipy-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cff3a5295234037e39500d35316a4c5794739433528310e117b8a9a0c76d20fc"}, + {file = "scipy-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:2318bef588acc7a574f5bfdff9c172d0b1bf2c8143d9582e05f878e580a3781e"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d644a64e174c16cb4b2e41dfea6af722053e83d066da7343f333a54dae9bc31c"}, + {file = "scipy-1.9.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:da8245491d73ed0a994ed9c2e380fd058ce2fa8a18da204681f2fe1f57f98f95"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4db5b30849606a95dcf519763dd3ab6fe9bd91df49eba517359e450a7d80ce2e"}, + {file = "scipy-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68db6b290cbd4049012990d7fe71a2abd9ffbe82c0056ebe0f01df8be5436b0"}, + {file = "scipy-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:5b88e6d91ad9d59478fafe92a7c757d00c59e3bdc3331be8ada76a4f8d683f58"}, + {file = "scipy-1.9.3.tar.gz", hash = "sha256:fbc5c05c85c1a02be77b1ff591087c83bc44579c6d2bd9fb798bb64ea5e1a027"}, +] + +[package.dependencies] +numpy = ">=1.18.5,<1.26.0" + +[package.extras] +dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"] +doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"] +test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + +[[package]] +name = "seaborn" +version = "0.12.2" +description = "Statistical data visualization" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "seaborn-0.12.2-py3-none-any.whl", hash = "sha256:ebf15355a4dba46037dfd65b7350f014ceb1f13c05e814eda2c9f5fd731afc08"}, + {file = "seaborn-0.12.2.tar.gz", hash = "sha256:374645f36509d0dcab895cba5b47daf0586f77bfe3b36c97c607db7da5be0139"}, +] + +[package.dependencies] +matplotlib = ">=3.1,<3.6.1 || >3.6.1" +numpy = ">=1.17,<1.24.0 || >1.24.0" +pandas = ">=0.25" + +[package.extras] +dev = ["flake8", "flit", "mypy", "pandas-stubs", "pre-commit", "pytest", "pytest-cov", "pytest-xdist"] +docs = ["ipykernel", "nbconvert", "numpydoc", "pydata_sphinx_theme (==0.10.0rc2)", "pyyaml", "sphinx-copybutton", "sphinx-design", "sphinx-issues"] +stats = ["scipy (>=1.3)", "statsmodels (>=0.10)"] + +[[package]] +name = "sentry-sdk" +version = "1.19.1" +description = "Python client for Sentry (https://sentry.io)" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "sentry-sdk-1.19.1.tar.gz", hash = "sha256:7ae78bd921981a5010ab540d6bdf3b793659a4db8cccf7f16180702d48a80d84"}, + {file = "sentry_sdk-1.19.1-py2.py3-none-any.whl", hash = "sha256:885a11c69df23e53eb281d003b9ff15a5bdfa43d8a2a53589be52104a1b4582f"}, +] + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)"] +grpcio = ["grpcio (>=1.21.1)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "67.6.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.6.1-py3-none-any.whl", hash = "sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078"}, + {file = "setuptools-67.6.1.tar.gz", hash = "sha256:257de92a9d50a60b8e22abfcbb771571fde0dbf3ec234463212027a4eeecbe9a"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "smmap" version = "5.0.0" @@ -400,6 +1590,10 @@ description = "A pure Python implementation of a sliding window memory map manag category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, +] [[package]] name = "starlette" @@ -408,21 +1602,41 @@ description = "The little ASGI library that shines." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"}, + {file = "starlette-0.14.2.tar.gz", hash = "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"}, +] [package.extras] full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] [[package]] name = "stevedore" -version = "4.0.0" +version = "5.0.0" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false python-versions = ">=3.8" +files = [ + {file = "stevedore-5.0.0-py3-none-any.whl", hash = "sha256:bd5a71ff5e5e5f5ea983880e4a1dd1bb47f8feebbb3d95b592398e2f02194771"}, + {file = "stevedore-5.0.0.tar.gz", hash = "sha256:2c428d2338976279e8eb2196f7a94910960d9f7ba2f41f3988511e95ca447021"}, +] [package.dependencies] pbr = ">=2.0.0,<2.1.0 || >2.1.0" +[[package]] +name = "threadpoolctl" +version = "3.1.0" +description = "threadpoolctl" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "threadpoolctl-3.1.0-py3-none-any.whl", hash = "sha256:8b99adda265feb6773280df41eece7b2e6561b772d21ffd52e372f999024907b"}, + {file = "threadpoolctl-3.1.0.tar.gz", hash = "sha256:a335baacfaa4400ae1f0d8e3a58d6674d2f8828e3716bb2802c44955ad391380"}, +] + [[package]] name = "toml" version = "0.10.2" @@ -430,34 +1644,80 @@ description = "Python Library for Tom's Obvious, Minimal Language" category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] [[package]] name = "typed-ast" -version = "1.4.3" +version = "1.4.0" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false python-versions = "*" +files = [ + {file = "typed_ast-1.4.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e"}, + {file = "typed_ast-1.4.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b"}, + {file = "typed_ast-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4"}, + {file = "typed_ast-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"}, + {file = "typed_ast-1.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631"}, + {file = "typed_ast-1.4.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233"}, + {file = "typed_ast-1.4.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1"}, + {file = "typed_ast-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a"}, + {file = "typed_ast-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c"}, + {file = "typed_ast-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a"}, + {file = "typed_ast-1.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e"}, + {file = "typed_ast-1.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d"}, + {file = "typed_ast-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36"}, + {file = "typed_ast-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0"}, + {file = "typed_ast-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66"}, + {file = "typed_ast-1.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2"}, + {file = "typed_ast-1.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47"}, + {file = "typed_ast-1.4.0-cp38-cp38-win32.whl", hash = "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161"}, + {file = "typed_ast-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e"}, + {file = "typed_ast-1.4.0.tar.gz", hash = "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34"}, +] [[package]] name = "typing-extensions" -version = "4.3.0" +version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] [[package]] name = "urllib3" -version = "1.26.10" +version = "1.26.15" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] [package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] @@ -467,6 +1727,10 @@ description = "The lightning-fast ASGI server." category = "main" optional = false python-versions = "*" +files = [ + {file = "uvicorn-0.14.0-py3-none-any.whl", hash = "sha256:2a76bb359171a504b3d1c853409af3adbfa5cef374a4a59e5881945a97a93eae"}, + {file = "uvicorn-0.14.0.tar.gz", hash = "sha256:45ad7dfaaa7d55cab4cd1e85e03f27e9d60bc067ddc59db52a2b0aeca8870292"}, +] [package.dependencies] asgiref = ">=3.3.4" @@ -474,7 +1738,7 @@ click = ">=7" h11 = ">=0.8" [package.extras] -standard = ["websockets (>=9.1)", "httptools (>=0.2.0,<0.3.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] +standard = ["PyYAML (>=5.1)", "colorama (>=0.4)", "httptools (>=0.2.0,<0.3.0)", "python-dotenv (>=0.13)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchgod (>=0.6)", "websockets (>=9.1)"] [[package]] name = "uvloop" @@ -483,268 +1747,7 @@ description = "Fast implementation of asyncio event loop on top of libuv" category = "main" optional = false python-versions = ">=3.7" - -[package.extras] -dev = ["Cython (>=0.29.20,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "aiohttp", "flake8 (>=3.8.4,<3.9.0)", "psutil", "pycodestyle (>=2.6.0,<2.7.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"] -docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)"] -test = ["aiohttp", "flake8 (>=3.8.4,<3.9.0)", "psutil", "pycodestyle (>=2.6.0,<2.7.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"] - -[[package]] -name = "wrapt" -version = "1.12.1" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "*" - -[metadata] -lock-version = "1.1" -python-versions = "^3.8" -content-hash = "c01b97e2fda031efbc5277fdfb65da75cef9b2da388aa7ecbfbe6d5a68e8dfdb" - -[metadata.files] -asgiref = [] -astroid = [ - {file = "astroid-2.5.6-py3-none-any.whl", hash = "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e"}, - {file = "astroid-2.5.6.tar.gz", hash = "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"}, -] -atomicwrites = [] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] -bandit = [] -certifi = [] -chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, -] -click = [] -colorama = [] -fastapi = [ - {file = "fastapi-0.65.3-py3-none-any.whl", hash = "sha256:d3e3c0ac35110efb22ee3ed28201cf32f9d11a9a0e52d7dd676cad25f5219523"}, - {file = "fastapi-0.65.3.tar.gz", hash = "sha256:6ea2286e439c4ced7cce2b2862c25859601bf327a515c12dd6e431ef5d49d12f"}, -] -flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] -gitdb = [ - {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, - {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, -] -gitpython = [] -gunicorn = [ - {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, - {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, -] -h11 = [] -idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -lazy-object-proxy = [ - {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, - {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mypy = [ - {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, - {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, - {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, - {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, - {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, - {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, - {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, - {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, - {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, - {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, - {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, - {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, - {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, - {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, - {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, - {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, - {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, - {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, - {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, - {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, - {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, - {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -orjson = [] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -pbr = [] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] -pydantic = [] -pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] -pylint = [ - {file = "pylint-2.8.3-py3-none-any.whl", hash = "sha256:792b38ff30903884e4a9eab814ee3523731abd3c463f3ba48d7b627e87013484"}, - {file = "pylint-2.8.3.tar.gz", hash = "sha256:0a049c5d47b629d9070c3932d13bff482b12119b6a241a93bc460b0be16953c8"}, -] -pyparsing = [] -pytest = [ - {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, - {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -requests = [ - {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, - {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, -] -smmap = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, -] -starlette = [ - {file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"}, - {file = "starlette-0.14.2.tar.gz", hash = "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"}, -] -stevedore = [] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -typed-ast = [ - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, - {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, - {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, - {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, - {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, - {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, - {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, - {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, - {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, - {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, -] -typing-extensions = [] -urllib3 = [] -uvicorn = [ - {file = "uvicorn-0.14.0-py3-none-any.whl", hash = "sha256:2a76bb359171a504b3d1c853409af3adbfa5cef374a4a59e5881945a97a93eae"}, - {file = "uvicorn-0.14.0.tar.gz", hash = "sha256:45ad7dfaaa7d55cab4cd1e85e03f27e9d60bc067ddc59db52a2b0aeca8870292"}, -] -uvloop = [ +files = [ {file = "uvloop-0.15.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e71fb9038bfcd7646ca126c5ef19b17e48d4af9e838b2bcfda7a9f55a6552a32"}, {file = "uvloop-0.15.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7522df4e45e4f25b50adbbbeb5bb9847495c438a628177099d2721f2751ff825"}, {file = "uvloop-0.15.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae2b325c0f6d748027f7463077e457006b4fdb35a8788f01754aadba825285ee"}, @@ -756,6 +1759,40 @@ uvloop = [ {file = "uvloop-0.15.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ff05116ede1ebdd81802df339e5b1d4cab1dfbd99295bf27e90b4cec64d70e9"}, {file = "uvloop-0.15.3.tar.gz", hash = "sha256:905f0adb0c09c9f44222ee02f6b96fd88b493478fffb7a345287f9444e926030"}, ] -wrapt = [ + +[package.extras] +dev = ["Cython (>=0.29.20,<0.30.0)", "Sphinx (>=1.7.3,<1.8.0)", "aiohttp", "flake8 (>=3.8.4,<3.9.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.6.0,<2.7.0)", "pytest (>=3.6.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)"] +docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinx-rtd-theme (>=0.2.4,<0.3.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)"] +test = ["aiohttp", "flake8 (>=3.8.4,<3.9.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=19.0.0,<19.1.0)", "pycodestyle (>=2.6.0,<2.7.0)"] + +[[package]] +name = "wrapt" +version = "1.12.1" +description = "Module for decorators, wrappers and monkey patching." +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, ] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "9fda923e6b333cf9fab10aa53b21b8ff11e4458f63d2f013048e141beec1bc0a" diff --git a/py-spy/ROB-Health.png b/py-spy/ROB-Health.png new file mode 100644 index 00000000..2f785e08 Binary files /dev/null and b/py-spy/ROB-Health.png differ diff --git a/py-spy/ROB-Popular.png b/py-spy/ROB-Popular.png new file mode 100644 index 00000000..159b7ab8 Binary files /dev/null and b/py-spy/ROB-Popular.png differ diff --git a/py-spy/ROB-helth.png b/py-spy/ROB-helth.png new file mode 100644 index 00000000..b7d23d5f Binary files /dev/null and b/py-spy/ROB-helth.png differ diff --git a/py-spy/ROB-popular-2.png b/py-spy/ROB-popular-2.png new file mode 100644 index 00000000..99ba4c2c Binary files /dev/null and b/py-spy/ROB-popular-2.png differ diff --git a/py-spy/ROBBM25.png b/py-spy/ROBBM25.png new file mode 100644 index 00000000..235715ba Binary files /dev/null and b/py-spy/ROBBM25.png differ diff --git a/py-spy/ROBBMP25.png b/py-spy/ROBBMP25.png new file mode 100644 index 00000000..f08e80f4 Binary files /dev/null and b/py-spy/ROBBMP25.png differ diff --git a/pyproject.toml b/pyproject.toml index ea68785b..b97480fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,17 @@ [tool.poetry] name = "reco_service" -version = "0.0.1" +version = "0.0.2" description = "" -authors = ["Emiliy Feldman "] -maintainers = ["Emiliy Feldman "] +authors = [ + "Shakirov Renat ", + "Mostovik Vladislav ", + "Zaraev Robert " +] +maintainers = [ + "Shakirov Renat ", + "Mostovik Vladislav ", + "Zaraev Robert " +] readme = "README.md" packages = [ { include = "service" } @@ -18,6 +26,12 @@ uvloop = "^0.15.2" uvicorn = "^0.14.0" orjson = "^3.7.7" starlette = "^0.14.2" +seaborn = "^0.12.1" +lightfm = "1.16" +hnswlib = "0.6.2" +pybind11 = "2.6.1" +nmslib = "^2.1.1" +sentry-sdk= "^1.19.1" [tool.poetry.dev-dependencies] pytest = "^6.2.4" @@ -27,7 +41,13 @@ isort = "^5.8.0" bandit = "^1.7.0" flake8 = "^3.9.2" pylint = "~2.8.3" +typed-ast = "1.4.0" [build-system] requires = ["poetry>=1.0.5"] build-backend = "poetry.masonry.api" + +#poetry lock --no-update +# make setup +# +# ARCHFLAGS="-arch arm64" pip install lightfm --compile --no-cache-dir diff --git a/sentry_pic/666 user.png b/sentry_pic/666 user.png new file mode 100644 index 00000000..4ccb3401 Binary files /dev/null and b/sentry_pic/666 user.png differ diff --git a/sentry_pic/LongUserId.png b/sentry_pic/LongUserId.png new file mode 100644 index 00000000..aba9bb71 Binary files /dev/null and b/sentry_pic/LongUserId.png differ diff --git a/sentry_pic/ModelError.png b/sentry_pic/ModelError.png new file mode 100644 index 00000000..cad24349 Binary files /dev/null and b/sentry_pic/ModelError.png differ diff --git a/service/api/config.py b/service/api/config.py new file mode 100644 index 00000000..d7ea4981 --- /dev/null +++ b/service/api/config.py @@ -0,0 +1,4 @@ +config_env = { + "API_KEY": "12345678", + "API_KEY_NAME": "API_KEY" +} diff --git a/service/api/exceptions.py b/service/api/exceptions.py index 3fc78268..a77b3d65 100644 --- a/service/api/exceptions.py +++ b/service/api/exceptions.py @@ -26,3 +26,25 @@ def __init__( error_loc: tp.Optional[tp.Sequence[str]] = None, ): super().__init__(status_code, error_key, error_message, error_loc) + + +class ModelNotFoundError(AppException): + def __init__( + self, + status_code: int = HTTPStatus.NOT_FOUND, + error_key: str = "model_not_found", + error_message: str = "Model is unknown", + error_loc: tp.Optional[tp.Sequence[str]] = None, + ): + super().__init__(status_code, error_key, error_message, error_loc) + + +class CredentialError(AppException): + def __init__( + self, + status_code: int = HTTPStatus.FORBIDDEN, + error_key: str = "wrong_credentials", + error_message: str = "Could not validate API KEY", + error_loc: tp.Optional[tp.Sequence[str]] = None, + ): + super().__init__(status_code, error_key, error_message, error_loc) diff --git a/service/api/models.py b/service/api/models.py new file mode 100644 index 00000000..9f76196b --- /dev/null +++ b/service/api/models.py @@ -0,0 +1,19 @@ +from typing import List, Optional, Sequence + +from pydantic import BaseModel + + +class RecoResponse(BaseModel): + user_id: int + items: List[int] + +class NotFoundError(BaseModel): + error_key: str + error_message: str = "NotFound" + error_loc: Optional[Sequence[str]] + + +class UnauthorizedError(BaseModel): + error_key: str + error_message: str = "Unauthorized" + error_loc: Optional[Sequence[str]] diff --git a/service/api/models_zoo.py b/service/api/models_zoo.py new file mode 100644 index 00000000..35aa87a9 --- /dev/null +++ b/service/api/models_zoo.py @@ -0,0 +1,563 @@ +from abc import ABC, abstractmethod +from collections import defaultdict +from functools import reduce +from typing import Dict, List, Optional, Set, Tuple + +import dill +import numpy as np +import pandas as pd +import scipy as sp +from implicit.nearest_neighbours import ItemItemRecommender +from lightfm import LightFM +from scipy.sparse import csr_matrix + + +class BaseModelZoo(ABC): + def __init__(self): + pass + + @staticmethod + def unique_reco(items: List[int]) -> List[int]: + seen: Set[int] = set() + seen_add = seen.add + return [item for item in items if not (item in seen or seen_add(item))] + + @abstractmethod + def reco_predict( + self, + user_id: int, + k_recs: int + ) -> List[int]: + """ + Main function for recommendation items to users + :param user_id: user identification + :param k_recs: how many recs do you need + :return: list of recommendation ids + """ + + +class DumpModel(BaseModelZoo): + def reco_predict( + self, + user_id: int, + k_recs: int + ) -> List[int]: + """ + Main function for recommendation items to users + :param user_id: user identification + :param k_recs: how many recs do you need + :return: list of recommendation ids + """ + reco = list(range(k_recs)) + return reco + + +class TopPopularAllCovered(BaseModelZoo): + def __init__( + self, + top_reco: Tuple[int, ...] = tuple([ + 10440, 15297, 9728, 13865, 2657, + 4151, 3734, 6809, 4740, 4880, 7571, + 11237, 8636, 14741 + ]) + ) -> None: + super().__init__() + self.top_reco = top_reco + + def reco_predict( + self, + user_id: int, + k_recs: int + ) -> List[int]: + """ + Main function for recommendation items to users + :param user_id: user identification + :param k_recs: how many recs do you need + :return: list of recommendation ids + """ + reco = list(self.top_reco)[:k_recs] + return reco + + +class Popular(BaseModelZoo): + def __init__( + self, + top_reco: Tuple[int, ...] = tuple([ + 10440, 15297, 9728, 13865, 4151, + 3734, 2657, 4880, 142, 6809 + ]) + ) -> None: + super().__init__() + self.top_reco = top_reco + + def reco_predict( + self, + user_id: int, + k_recs: int + ) -> List[int]: + """ + Main function for recommendation items to users + :param user_id: user identification + :param k_recs: how many recs do you need + :return: list of recommendation ids + """ + reco = list(self.top_reco)[:k_recs] + return reco + + +class KNNModelWithTop(BaseModelZoo): + def __init__( + self, + path_to_reco: str = "data/BlendingKNNWithAddFeatures.csv.gz", + top_reco: Tuple[int, ...] = tuple([ + 10440, 15297, 9728, 13865, 3734, + 12192, 4151, 11863, 7793, 7829 + ]), + ) -> None: + super().__init__() + self.path_to_reco = path_to_reco + if self.path_to_reco.endswith('csv.gz'): + self.data = pd.read_csv(path_to_reco, compression='gzip') + elif self.path_to_reco.endswith('.csv'): + self.data = pd.read_csv(path_to_reco) + self.top_reco = top_reco + + def reco_predict( + self, + user_id: int, + k_recs: int + ) -> List[int]: + """ + Main function for recommendation items to users + :param user_id: user identification + :param k_recs: how many recs do you need + :return: list of recommendation ids + """ + reco = ( + self.data[self.data.user_id == user_id] + .item_id + .tolist() + [:k_recs] + ) + + if len(reco) < k_recs: + reco.extend(self.top_reco) + reco = self.unique_reco(reco)[:k_recs] # Удаляем дубли + + return reco + + +class OnlineModel(BaseModelZoo): + def __init__( + self, + path_to_model: str = "data/knn_bm25.pickle", + top_reco: Tuple[int, ...] = tuple([ + 10440, 15297, 9728, 13865, 3734, + 12192, 4151, 11863, 7793, 7829 + ]) + ) -> None: + super().__init__() + + with open(path_to_model, 'rb') as f: + self.model = dill.load(f) + + self.top_reco = top_reco + + def reco_predict( + self, + user_id: int, + k_recs: int + ) -> List[int]: + """ + Main function for recommendation items to users + :param user_id: user identification + :param k_recs: how many recs do you need + :return: list of recommendation ids + """ + try: + reco = self.model.predict(user_id=user_id) + except KeyError: + reco = [] + + if len(reco) < k_recs: + reco.extend(self.top_reco) + reco = self.unique_reco(reco)[:k_recs] # Удаляем дубли + + return reco + + +class UserKNN: + """ + Class for fit-perdict UserKNN model and BM25 + based on ItemKNN model from implicit.nearest_neighbours + """ + + def __init__( + self, + dist_model: ItemItemRecommender, + n_neighbors: int = 50, + verbose: int = 1, + ): + self.n_neighbors = n_neighbors + self.dist_model = dist_model + self.verbose = verbose + self.is_fitted = False + + self.mapping: Dict[str, Dict[int, int]] = defaultdict(dict) + + self.weights_matrix = None + self.users_watched = None + + def get_mappings(self, train): + self.mapping['users_inv_mapping'] = dict( + enumerate(train['user_id'].unique()) + ) + self.mapping['users_mapping'] = { + v: k for k, v in self.mapping['users_inv_mapping'].items() + } + + self.mapping['items_inv_mapping'] = dict( + enumerate(train['item_id'].unique()) + ) + self.mapping['items_mapping'] = { + v: k for k, v in self.mapping['items_inv_mapping'].items() + } + + def get_matrix( + self, df: pd.DataFrame, + user_col: str = 'user_id', + item_col: str = 'item_id', + weight_col: str = None, + ): + if weight_col: + weights = df[weight_col].astype(np.float32) + else: + weights = np.ones(len(df), dtype=np.float32) + + if hasattr(self.mapping['users_mapping'], 'get') and \ + hasattr(self.mapping['items_mapping'], 'get'): + interaction_matrix = sp.sparse.coo_matrix(( + weights, + ( + df[user_col].map(self.mapping['users_mapping'].get), + df[item_col].map(self.mapping['items_mapping'].get) + ) + )) + else: + raise AttributeError + + self.users_watched = df.groupby(user_col).agg({item_col: list}) + return interaction_matrix + + def fit(self, train: pd.DataFrame): + self.get_mappings(train) + self.weights_matrix = self.get_matrix(train).tocsr().T + + self.dist_model.fit( + self.weights_matrix, + show_progress=(self.verbose > 0) + ) + self.is_fitted = True + + @staticmethod + def _generate_recs_mapper( + model: ItemItemRecommender, + user_mapping: Dict[int, int], + user_inv_mapping: Dict[int, int], + n_neighbors: int + ): + def _recs_mapper(user): + user_id = user_mapping[user] + recs = model.similar_items(user_id, N=n_neighbors) + return ( + [user_inv_mapping[user] for user, _ in zip(*recs)], + [sim for _, sim in zip(*recs)] + ) + + return _recs_mapper + + def predict(self, user_id: int, n_recs: int = 10): + + if not self.is_fitted: + raise ValueError("Fit model before predicting") + + mapper = self._generate_recs_mapper( + model=self.dist_model, + user_mapping=self.mapping['users_mapping'], + user_inv_mapping=self.mapping['users_inv_mapping'], + n_neighbors=self.n_neighbors + ) + + recs = pd.DataFrame({'user_id': [user_id]}) + + try: + recs['sim_user_id'], recs['sim'] = zip( + *recs['user_id'].map(mapper) + ) + except AttributeError: + return [] + + recs = recs.set_index('user_id').apply(pd.Series.explode).reset_index() + + recs = ( + recs + .merge( + self.users_watched, + left_on=['sim_user_id'], + right_on=['user_id'], how='left' + ) + .explode('item_id') + .sort_values(['user_id', 'sim'], ascending=False) + .drop_duplicates(['user_id', 'item_id'], keep='first') + ) + + recs['rank'] = recs.groupby('user_id').cumcount() + 1 + + result = recs[recs['rank'] <= n_recs][['user_id', 'item_id', 'rank']] + + return result.item_id.tolist()[:n_recs] + + +class LightFMWrapper: + """ + Class for fit-predict LightFM + """ + def __init__( + self, + random_state: int = 42, + learning_rate: float = 0.05, + no_components: int = 10, + item_alpha: float = 0, + user_alpha: float = 0, + loss: str = 'warp', + ): + + self.is_fitted = False + + self.mapping: Dict[str, Dict[int, int]] = defaultdict(dict) + + self.weights_matrix = None + self.users_watched = None + + self.user_features = None + self.item_features = None + + self.model = LightFM( + loss=loss, + no_components=no_components, + user_alpha=user_alpha, + item_alpha=item_alpha, + random_state=random_state, + learning_rate=learning_rate, + ) + + def get_mappings(self, users, items): + self.mapping['users_inv_mapping'] = dict( + enumerate(users['user_id'].unique()) + ) + self.mapping['users_mapping'] = dict({ + v: k for k, v in self.mapping['users_inv_mapping'].items() + }) + + self.mapping['items_inv_mapping'] = dict( + enumerate(items['item_id'].unique()) + ) + self.mapping['items_mapping'] = dict({ + v: k for k, v in self.mapping['items_inv_mapping'].items() + }) + + def get_matrix( + self, df: pd.DataFrame, + user_col: str = 'user_id', + item_col: str = 'item_id', + weight_col: str = None, + ): + if weight_col: + weights = df[weight_col].astype(np.float32) + else: + weights = np.ones(len(df), dtype=np.float32) + + if hasattr(self.mapping['users_mapping'], 'get') and \ + hasattr(self.mapping['items_mapping'], 'get'): + interaction_matrix = sp.sparse.coo_matrix(( + weights, + ( + df[user_col].map(self.mapping['users_mapping'].get), + df[item_col].map(self.mapping['items_mapping'].get) + ) + )) + else: + raise AttributeError + + self.users_watched = df.groupby(user_col).agg({item_col: list}) + return interaction_matrix + + def prepare_additional_features_users(self, features: pd.DataFrame): + user_features_frames = [] + for feature in ["sex", "age", "income"]: + feature_frame = features.reindex(columns=['user_id', feature]) + feature_frame.columns = ["id", "value"] + feature_frame["feature"] = feature + user_features_frames.append(feature_frame) + user_features = pd.concat(user_features_frames) + + user_final_features = pd.get_dummies( + user_features[['id', 'value']]).groupby('id', as_index=False).sum() + + user_final_features.id = user_final_features.id.map( + self.mapping['items_mapping'] + ) + user_final_features = user_final_features.sort_values('id').drop( + 'id', + axis=1 + ) + user_final_features = csr_matrix(user_final_features.values) + + return user_final_features + + def prepare_additional_features_items(self, features: pd.DataFrame): + features.fillna('Unknown', inplace=True) + features["genre"] = features["genres"].str.lower().str.replace( + ", ", + ",", + regex=False + ).str.split(",") + features["actors"] = features["actors"].str.lower().str.replace( + ", ", + ",", + regex=False + ).str.split(",") + + genre_feature = features[["item_id", "genre"]].explode("genre") + genre_feature.columns = ["id", "value"] + genre_feature["feature"] = "genre" + + actors_feature = features[["item_id", "actors"]].explode("actors") + actors_feature.columns = ["id", "value"] + actors_feature["feature"] = "actors" + + content_feature = features.reindex(columns=['item_id', "content_type"]) + content_feature.columns = ["id", "value"] + content_feature["feature"] = "content_type" + + country_feature = features.reindex(columns=['item_id', "countries"]) + country_feature.columns = ["id", "value"] + country_feature["feature"] = "countries" + + age_feature = features.reindex(columns=['item_id', "age_rating"]) + age_feature.columns = ["id", "value"] + age_feature["feature"] = "age_feature" + + studios_feature = features.reindex(columns=['item_id', "studios"]) + studios_feature.columns = ["id", "value"] + studios_feature["feature"] = "studios" + + genre_feature_bin = ( + pd.get_dummies(genre_feature[['id', 'value']]) + .groupby('id', as_index=False) + .sum() + ) + content_feature_bin = ( + pd.get_dummies(content_feature[['id', 'value']]) + .groupby('id', as_index=False) + .sum() + ) + studios_feature_bin = ( + pd.get_dummies(studios_feature[['id', 'value']]) + .groupby('id', as_index=False) + .sum() + ) + age_feature_bin = ( + pd.get_dummies(age_feature[['id', 'value']]) + .groupby('id', as_index=False) + .sum() + ) + country_feature_bin = ( + pd.get_dummies(country_feature[['id', 'value']]) + .groupby('id', as_index=False) + .sum() + ) + + dfs = [ + genre_feature_bin, + content_feature_bin, + studios_feature_bin, + age_feature_bin, + country_feature_bin + ] + item_final_features = reduce( + lambda left, right: pd.merge(left, right, on='id'), + dfs + ) + + item_final_features.id = item_final_features.id.map( + self.mapping['items_mapping'] + ) + item_final_features = ( + item_final_features + .sort_values('id') + .drop('id', axis=1) + ) + item_final_features_matrix_sparse = csr_matrix( + item_final_features.values) + + return item_final_features_matrix_sparse + + def fit( + self, + train: pd.DataFrame, + user_features: Optional[pd.DataFrame] = None, + # если не none, то делаем фичи + item_features: Optional[pd.DataFrame] = None, + # если не none, то делаем фичи + epochs: int = 1, + num_threads: int = 8, + verbose: int = 1, + + ): + if user_features is not None: + self.user_features = self.prepare_additional_features_users( + user_features) + + if item_features is not None: + self.item_features = self.prepare_additional_features_items( + item_features) + + self.get_mappings(user_features, item_features) + self.weights_matrix = self.get_matrix(train).tocsr() + + self.model.fit( + self.weights_matrix, + epochs=epochs, + user_features=self.user_features, + # csr_matrix of shape [n_users, n_user_features] + item_features=self.item_features, + # csr_matrix of shape [n_items, n_item_features] + num_threads=num_threads, + verbose=verbose > 0, + ) + + self.is_fitted = True + + def predict(self, user_id: int, n_recs: int = 10): + + if not self.is_fitted: + raise ValueError("Fit model before predicting") + + if user_id not in self.mapping['users_mapping'].keys(): + return [] + user_id = self.mapping['users_mapping'][user_id] + + scores = self.model.predict( + user_id, + np.arange(len(self.mapping['items_mapping'])), + item_features=self.item_features, + user_features=self.user_features + ) # LightFM + top_items = np.argsort(-scores)[:n_recs] + reco = [ + self.mapping['items_inv_mapping'][inv_reco_item] + for inv_reco_item in top_items + ] + + return reco diff --git a/service/api/views.py b/service/api/views.py index 24cf4a7f..e93a5bad 100644 --- a/service/api/views.py +++ b/service/api/views.py @@ -1,25 +1,62 @@ -from typing import List +from fastapi import APIRouter, Depends, FastAPI, Request, Security +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer +from fastapi.security.api_key import APIKey, APIKeyHeader, APIKeyQuery -from fastapi import APIRouter, FastAPI, Request -from pydantic import BaseModel - -from service.api.exceptions import UserNotFoundError +from service.api.exceptions import ( + CredentialError, + ModelNotFoundError, + UserNotFoundError, +) from service.log import app_logger +from .config import config_env +from .models import NotFoundError, RecoResponse, UnauthorizedError +from .models_zoo import DumpModel, OnlineModel, Popular -class RecoResponse(BaseModel): - user_id: int - items: List[int] - +import sentry_sdk router = APIRouter() +api_query = APIKeyQuery(name=config_env["API_KEY_NAME"], auto_error=False) +api_header = APIKeyHeader(name=config_env["API_KEY_NAME"], auto_error=False) +token_bearer = HTTPBearer(auto_error=False) -@router.get( - path="/health", - tags=["Health"], +sentry_sdk.init( + dsn="https://687935e2f3184212a6eeea6eb4784c7d@o4504984015798272.ingest" + + ".sentry.io/4504984017305600", + traces_sample_rate=1.0, ) -async def health() -> str: + +try: + models_zoo = { + "model_1": DumpModel(), + "popular" : Popular(), + "LightFM": OnlineModel(path_to_model='./data/lightfm.pickle') + } +except FileNotFoundError: + models_zoo = { + "model_1": DumpModel(), + "popular": Popular(), + } + + +async def get_api_key( + api_key_query: str = Security(api_query), + api_key_header: str = Security(api_header), + token: HTTPAuthorizationCredentials = Security(token_bearer), +): + if api_key_query == config_env["API_KEY"]: + return api_key_query + if api_key_header == config_env["API_KEY"]: + return api_key_header + if token is not None and token.credentials == config_env["API_KEY"]: + return token.credentials + sentry_sdk.capture_exception(error='Token Error') + raise CredentialError() + + +@router.get(path="/health", tags=["Health"]) +async def health(api_key: APIKey = Depends(get_api_key)) -> str: return "I am alive" @@ -27,21 +64,43 @@ async def health() -> str: path="/reco/{model_name}/{user_id}", tags=["Recommendations"], response_model=RecoResponse, + responses={ + 404: {"model": NotFoundError, "user": NotFoundError}, + 401: {"model": UnauthorizedError}, + }, ) async def get_reco( request: Request, model_name: str, user_id: int, + api_key: APIKey = Depends(get_api_key), ) -> RecoResponse: app_logger.info(f"Request for model: {model_name}, user_id: {user_id}") - # Write your code here + if user_id > 10 ** 6: + sentry_sdk.capture_exception( + error=f"User {user_id} not found") + raise UserNotFoundError( + error_message=f"User {user_id} not found") - if user_id > 10**9: - raise UserNotFoundError(error_message=f"User {user_id} not found") + if user_id % 666 == 0: + sentry_sdk.capture_exception( + error=f"User id {user_id} is divided entirely into 666") + raise UserNotFoundError( + error_message=f"User id {user_id} is divided entirely into 666") + + if model_name not in models_zoo.keys(): + sentry_sdk.capture_exception( + error=f"Model {model_name} not found") + raise ModelNotFoundError(error_message=f"Model {model_name} not found") k_recs = request.app.state.k_recs - reco = list(range(k_recs)) + + reco = models_zoo[model_name].reco_predict( + user_id=user_id, + k_recs=k_recs + ) + return RecoResponse(user_id=user_id, items=reco) diff --git a/tests/api/test_views.py b/tests/api/test_views.py index 50516b47..60072101 100644 --- a/tests/api/test_views.py +++ b/tests/api/test_views.py @@ -2,16 +2,18 @@ from starlette.testclient import TestClient +from service.api.config import config_env from service.settings import ServiceConfig GET_RECO_PATH = "/reco/{model_name}/{user_id}" +HEADER = {config_env['API_KEY_NAME']: config_env["API_KEY"]} def test_health( client: TestClient, ) -> None: with client: - response = client.get("/health") + response = client.get("/health", headers=HEADER) assert response.status_code == HTTPStatus.OK @@ -20,9 +22,9 @@ def test_get_reco_success( service_config: ServiceConfig, ) -> None: user_id = 123 - path = GET_RECO_PATH.format(model_name="some_model", user_id=user_id) + path = GET_RECO_PATH.format(model_name="model_1", user_id=user_id) with client: - response = client.get(path) + response = client.get(path, headers=HEADER) assert response.status_code == HTTPStatus.OK response_json = response.json() assert response_json["user_id"] == user_id @@ -33,9 +35,34 @@ def test_get_reco_success( def test_get_reco_for_unknown_user( client: TestClient, ) -> None: - user_id = 10**10 + user_id = 10 ** 10 path = GET_RECO_PATH.format(model_name="some_model", user_id=user_id) with client: - response = client.get(path) + response = client.get(path, headers=HEADER) assert response.status_code == HTTPStatus.NOT_FOUND assert response.json()["errors"][0]["error_key"] == "user_not_found" + + +def test_get_reco_for_unknown_model( + client: TestClient, +) -> None: + user_id = 1 + model_name = 'model_2' + path = GET_RECO_PATH.format(model_name=model_name, user_id=user_id) + with client: + response = client.get(path, headers=HEADER) + assert response.status_code == HTTPStatus.NOT_FOUND + assert response.json()["errors"][0]["error_key"] == "model_not_found" + + +def test_get_reco_with_wrong_cred( + client: TestClient, +) -> None: + user_id = 123 + model_name = 'model_1' + path = GET_RECO_PATH.format(model_name=model_name, user_id=user_id) + wrong_header = {config_env['API_KEY_NAME']: "random_key"} + with client: + response = client.get(path, headers=wrong_header) + assert response.status_code == HTTPStatus.FORBIDDEN + assert response.json()["errors"][0]["error_key"] == "wrong_credentials"