diff --git a/.gitignore b/.gitignore
index cd56dce1..186220be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,11 @@
## OS configs
.DS_Store
+# Project
+data/*
+models/*
+reports/*
+
# Python
__pycache__
.ipynb_checkpoints
diff --git a/README.md b/README.md
index 83039e3a..b7bd11e6 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
### 1. Fork / Clone this repository
```bash
-git clone https://gitlab.com/iterative.ai/cse/tutorials/course-ds-base.git
+git clone https://github.com/iterative/course-ds-base.git
cd course-ds-base
```
@@ -15,6 +15,7 @@ cd course-ds-base
Create virtual environment named `dvc-venv` (you may use other name)
```bash
python3 -m venv dvc-venv
+echo "export PYTHONPATH=$PWD" >> dvc-venv/bin/activate
source dvc-venv/bin/activate
```
Install python libraries
@@ -30,9 +31,13 @@ Add Virtual Environment to Jupyter Notebook
python -m ipykernel install --user --name=dvc-venv
```
-Configure ToC for jupyter notebook (optional)
+Configure ToC for jupyter notebook (optional)/Install the python package
```bash
+
+
+pip install jupyter_contrib_nbextensions
+
jupyter contrib nbextension install --user
jupyter nbextension enable toc2/main
```
diff --git a/file.txt b/file.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/lineapy-trial-prototype.ipynb b/lineapy-trial-prototype.ipynb
new file mode 100644
index 00000000..a1b86c0b
--- /dev/null
+++ b/lineapy-trial-prototype.ipynb
@@ -0,0 +1,1228 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: lineapy in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (0.2.3)\n",
+ "Requirement already satisfied: jinja2 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (2.11.2)\n",
+ "Requirement already satisfied: pandas in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (1.3.2)\n",
+ "Requirement already satisfied: pydantic in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (1.8.2)\n",
+ "Requirement already satisfied: networkx in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (2.5)\n",
+ "Requirement already satisfied: SQLAlchemy<2.0.0,>=1.4 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (1.4.47)\n",
+ "Requirement already satisfied: requests in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (2.25.1)\n",
+ "Requirement already satisfied: alembic==1.8.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (1.8.0)\n",
+ "Requirement already satisfied: IPython>=7.0.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (7.19.0)\n",
+ "Requirement already satisfied: isort in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (5.9.3)\n",
+ "Requirement already satisfied: rich in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (12.4.4)\n",
+ "Requirement already satisfied: click>=8.0.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (8.1.3)\n",
+ "Requirement already satisfied: pyyaml in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (5.3.1)\n",
+ "Requirement already satisfied: fsspec in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (2022.7.1)\n",
+ "Requirement already satisfied: nbconvert<7.0.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (6.0.7)\n",
+ "Requirement already satisfied: nbformat in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (5.0.8)\n",
+ "Requirement already satisfied: cloudpickle in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (1.6.0)\n",
+ "Requirement already satisfied: asttokens in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (2.2.1)\n",
+ "Requirement already satisfied: black in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (21.7b0)\n",
+ "Requirement already satisfied: typing-extensions>=4.0.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from lineapy) (4.3.0)\n",
+ "Requirement already satisfied: importlib-metadata in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from alembic==1.8.0->lineapy) (2.0.0)\n",
+ "Requirement already satisfied: Mako in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from alembic==1.8.0->lineapy) (1.2.4)\n",
+ "Requirement already satisfied: importlib-resources in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from alembic==1.8.0->lineapy) (5.7.1)\n",
+ "Requirement already satisfied: appnope in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (0.1.0)\n",
+ "Requirement already satisfied: jedi>=0.10 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (0.17.1)\n",
+ "Requirement already satisfied: pygments in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (2.7.2)\n",
+ "Requirement already satisfied: pickleshare in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (0.7.5)\n",
+ "Requirement already satisfied: traitlets>=4.2 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (5.0.5)\n",
+ "Requirement already satisfied: pexpect>4.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (4.8.0)\n",
+ "Requirement already satisfied: decorator in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (4.4.2)\n",
+ "Requirement already satisfied: backcall in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (0.2.0)\n",
+ "Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (3.0.8)\n",
+ "Requirement already satisfied: setuptools>=18.5 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from IPython>=7.0.0->lineapy) (50.3.1.post20201107)\n",
+ "Requirement already satisfied: bleach in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (3.2.1)\n",
+ "Requirement already satisfied: testpath in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (0.4.4)\n",
+ "Requirement already satisfied: jupyter-core in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (4.6.3)\n",
+ "Requirement already satisfied: pandocfilters>=1.4.1 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (1.4.3)\n",
+ "Requirement already satisfied: defusedxml in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (0.6.0)\n",
+ "Requirement already satisfied: mistune<2,>=0.8.1 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (0.8.4)\n",
+ "Requirement already satisfied: jupyterlab-pygments in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (0.1.2)\n",
+ "Requirement already satisfied: nbclient<0.6.0,>=0.5.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (0.5.1)\n",
+ "Requirement already satisfied: entrypoints>=0.2.2 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbconvert<7.0.0->lineapy) (0.3)\n",
+ "Requirement already satisfied: MarkupSafe>=0.23 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from jinja2->lineapy) (1.1.1)\n",
+ "Requirement already satisfied: jsonschema!=2.5.0,>=2.4 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbformat->lineapy) (3.2.0)\n",
+ "Requirement already satisfied: ipython-genutils in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbformat->lineapy) (0.2.0)\n",
+ "Requirement already satisfied: greenlet!=0.4.17 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from SQLAlchemy<2.0.0,>=1.4->lineapy) (2.0.2)\n",
+ "Requirement already satisfied: six in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from asttokens->lineapy) (1.15.0)\n",
+ "Requirement already satisfied: tomli<2.0.0,>=0.2.6 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from black->lineapy) (1.2.3)\n",
+ "Requirement already satisfied: regex>=2020.1.8 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from black->lineapy) (2020.10.15)\n",
+ "Requirement already satisfied: mypy-extensions>=0.4.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from black->lineapy) (0.4.3)\n",
+ "Requirement already satisfied: appdirs in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from black->lineapy) (1.4.4)\n",
+ "Requirement already satisfied: pathspec<1,>=0.8.1 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from black->lineapy) (0.9.0)\n",
+ "Requirement already satisfied: python-dateutil>=2.7.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from pandas->lineapy) (2.8.1)\n",
+ "Requirement already satisfied: pytz>=2017.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from pandas->lineapy) (2022.1)\n",
+ "Requirement already satisfied: numpy>=1.17.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from pandas->lineapy) (1.18.5)\n",
+ "Requirement already satisfied: idna<3,>=2.5 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from requests->lineapy) (2.10)\n",
+ "Requirement already satisfied: certifi>=2017.4.17 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from requests->lineapy) (2020.6.20)\n",
+ "Requirement already satisfied: chardet<5,>=3.0.2 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from requests->lineapy) (3.0.4)\n",
+ "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from requests->lineapy) (1.25.11)\n",
+ "Requirement already satisfied: commonmark<0.10.0,>=0.9.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from rich->lineapy) (0.9.1)\n",
+ "Requirement already satisfied: parso<0.8.0,>=0.7.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from jedi>=0.10->IPython>=7.0.0->lineapy) (0.7.0)\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: attrs>=17.4.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat->lineapy) (20.3.0)\n",
+ "Requirement already satisfied: pyrsistent>=0.14.0 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat->lineapy) (0.17.3)\n",
+ "Requirement already satisfied: jupyter-client>=6.1.5 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbclient<0.6.0,>=0.5.0->nbconvert<7.0.0->lineapy) (6.1.7)\n",
+ "Requirement already satisfied: async-generator in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbclient<0.6.0,>=0.5.0->nbconvert<7.0.0->lineapy) (1.10)\n",
+ "Requirement already satisfied: nest-asyncio in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from nbclient<0.6.0,>=0.5.0->nbconvert<7.0.0->lineapy) (1.5.1)\n",
+ "Requirement already satisfied: ptyprocess>=0.5 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from pexpect>4.3->IPython>=7.0.0->lineapy) (0.6.0)\n",
+ "Requirement already satisfied: wcwidth in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->IPython>=7.0.0->lineapy) (0.2.5)\n",
+ "Requirement already satisfied: webencodings in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from bleach->nbconvert<7.0.0->lineapy) (0.5.1)\n",
+ "Requirement already satisfied: packaging in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from bleach->nbconvert<7.0.0->lineapy) (20.4)\n",
+ "Requirement already satisfied: zipp>=0.5 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from importlib-metadata->alembic==1.8.0->lineapy) (3.4.0)\n",
+ "Requirement already satisfied: tornado>=4.1 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from jupyter-client>=6.1.5->nbclient<0.6.0,>=0.5.0->nbconvert<7.0.0->lineapy) (6.1)\n",
+ "Requirement already satisfied: pyzmq>=13 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from jupyter-client>=6.1.5->nbclient<0.6.0,>=0.5.0->nbconvert<7.0.0->lineapy) (19.0.2)\n",
+ "Requirement already satisfied: pyparsing>=2.0.2 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from packaging->bleach->nbconvert<7.0.0->lineapy) (2.4.7)\n",
+ "\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.0.1\u001b[0m\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
+ ]
+ }
+ ],
+ "source": [
+ "! pip install lineapy"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: pandas==1.3.2 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (1.3.2)\n",
+ "Requirement already satisfied: python-dateutil>=2.7.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from pandas==1.3.2) (2.8.1)\n",
+ "Requirement already satisfied: numpy>=1.17.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from pandas==1.3.2) (1.18.5)\n",
+ "Requirement already satisfied: pytz>=2017.3 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from pandas==1.3.2) (2022.1)\n",
+ "Requirement already satisfied: six>=1.5 in /Users/jenif/opt/anaconda3/lib/python3.8/site-packages (from python-dateutil>=2.7.3->pandas==1.3.2) (1.15.0)\n",
+ "\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.0.1\u001b[0m\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
+ ]
+ }
+ ],
+ "source": [
+ "! python -m pip install pandas==1.3.2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%load_ext lineapy"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "lineapy_config(home_dir=PosixPath('/Users/jenif/.lineapy'), database_url='sqlite:////Users/jenif/.lineapy/db.sqlite', artifact_storage_dir=PosixPath('/Users/jenif/.lineapy/linea_pickles'), customized_annotation_folder=PosixPath('/Users/jenif/.lineapy/custom-annotations'), do_not_track=False, logging_level='INFO', logging_file=PosixPath('/Users/jenif/.lineapy/lineapy.log'), storage_options=None, mlflow_registry_uri=None, mlflow_tracking_uri=None, default_ml_models_storage_backend=None)"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "lineapy.options"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:17:31.460557Z",
+ "start_time": "2019-06-16T21:17:29.395297Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import lineapy\n",
+ "import joblib\n",
+ "import json\n",
+ "import itertools\n",
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "from sklearn.metrics import confusion_matrix, f1_score\n",
+ "from sklearn.linear_model import LogisticRegression\n",
+ "from sklearn.model_selection import train_test_split\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Load dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:17:31.485189Z",
+ "start_time": "2019-06-16T21:17:31.473720Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " sepal length (cm) | \n",
+ " sepal width (cm) | \n",
+ " petal length (cm) | \n",
+ " petal width (cm) | \n",
+ " target | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 5.1 | \n",
+ " 3.5 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 4.9 | \n",
+ " 3.0 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 4.7 | \n",
+ " 3.2 | \n",
+ " 1.3 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 4.6 | \n",
+ " 3.1 | \n",
+ " 1.5 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 5.0 | \n",
+ " 3.6 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) \\\n",
+ "0 5.1 3.5 1.4 0.2 \n",
+ "1 4.9 3.0 1.4 0.2 \n",
+ "2 4.7 3.2 1.3 0.2 \n",
+ "3 4.6 3.1 1.5 0.2 \n",
+ "4 5.0 3.6 1.4 0.2 \n",
+ "\n",
+ " target \n",
+ "0 0 \n",
+ "1 0 \n",
+ "2 0 \n",
+ "3 0 \n",
+ "4 0 "
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Get data \n",
+ "\n",
+ "import pandas as pd\n",
+ "from sklearn.datasets import load_iris\n",
+ "\n",
+ "data = load_iris(as_frame=True)\n",
+ "dataset = data.frame\n",
+ "dataset.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0: setosa\n",
+ "1: versicolor\n",
+ "2: virginica\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "[None, None, None]"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# print labels for target values \n",
+ "\n",
+ "[print(f'{target}: {label}') for target, label in zip(data.target.unique(), data.target_names)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:17:32.328046Z",
+ "start_time": "2019-06-16T21:17:32.323611Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['sepal_length', 'sepal_width', 'petal_length', 'petal_width']"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# feature names\n",
+ "\n",
+ "dataset.columns = [colname.strip(' (cm)').replace(' ', '_') for colname in dataset.columns.tolist()]\n",
+ "\n",
+ "feature_names = dataset.columns.tolist()[:4]\n",
+ "feature_names"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#save raw data as artifact\n",
+ "dataset_csv = './data/raw/iris.csv'\n",
+ "dataset.to_csv(dataset_csv, index=False)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " sepal_length | \n",
+ " sepal_width | \n",
+ " petal_length | \n",
+ " petal_width | \n",
+ " target | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 5.1 | \n",
+ " 3.5 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 4.9 | \n",
+ " 3.0 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 4.7 | \n",
+ " 3.2 | \n",
+ " 1.3 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 4.6 | \n",
+ " 3.1 | \n",
+ " 1.5 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 5.0 | \n",
+ " 3.6 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " | 145 | \n",
+ " 6.7 | \n",
+ " 3.0 | \n",
+ " 5.2 | \n",
+ " 2.3 | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ " | 146 | \n",
+ " 6.3 | \n",
+ " 2.5 | \n",
+ " 5.0 | \n",
+ " 1.9 | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ " | 147 | \n",
+ " 6.5 | \n",
+ " 3.0 | \n",
+ " 5.2 | \n",
+ " 2.0 | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ " | 148 | \n",
+ " 6.2 | \n",
+ " 3.4 | \n",
+ " 5.4 | \n",
+ " 2.3 | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ " | 149 | \n",
+ " 5.9 | \n",
+ " 3.0 | \n",
+ " 5.1 | \n",
+ " 1.8 | \n",
+ " 2 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
150 rows × 5 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sepal_length sepal_width petal_length petal_width target\n",
+ "0 5.1 3.5 1.4 0.2 0\n",
+ "1 4.9 3.0 1.4 0.2 0\n",
+ "2 4.7 3.2 1.3 0.2 0\n",
+ "3 4.6 3.1 1.5 0.2 0\n",
+ "4 5.0 3.6 1.4 0.2 0\n",
+ ".. ... ... ... ... ...\n",
+ "145 6.7 3.0 5.2 2.3 2\n",
+ "146 6.3 2.5 5.0 1.9 2\n",
+ "147 6.5 3.0 5.2 2.0 2\n",
+ "148 6.2 3.4 5.4 2.3 2\n",
+ "149 5.9 3.0 5.1 1.8 2\n",
+ "\n",
+ "[150 rows x 5 columns]"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1.3.2\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(pd.__version__)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "LineaArtifact(name='iris-raw', _version=4)"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#save raw data as artifact to lineapy\n",
+ "lineapy.save(dataset, \"iris-raw\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Features engineering"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:02.150708Z",
+ "start_time": "2019-06-16T21:21:02.144518Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "dataset['sepal_length_to_sepal_width'] = dataset['sepal_length'] / dataset['sepal_width']\n",
+ "dataset['petal_length_to_petal_width'] = dataset['petal_length'] / dataset['petal_width']\n",
+ "\n",
+ "dataset = dataset[[\n",
+ " 'sepal_length', 'sepal_width', 'petal_length', 'petal_width',\n",
+ "# 'sepal_length_in_square', 'sepal_width_in_square', 'petal_length_in_square', 'petal_width_in_square',\n",
+ " 'sepal_length_to_sepal_width', 'petal_length_to_petal_width',\n",
+ " 'target'\n",
+ "]]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:02.987144Z",
+ "start_time": "2019-06-16T21:21:02.976092Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " sepal_length | \n",
+ " sepal_width | \n",
+ " petal_length | \n",
+ " petal_width | \n",
+ " sepal_length_to_sepal_width | \n",
+ " petal_length_to_petal_width | \n",
+ " target | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 5.1 | \n",
+ " 3.5 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 1.457143 | \n",
+ " 7.0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 4.9 | \n",
+ " 3.0 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 1.633333 | \n",
+ " 7.0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 4.7 | \n",
+ " 3.2 | \n",
+ " 1.3 | \n",
+ " 0.2 | \n",
+ " 1.468750 | \n",
+ " 6.5 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 4.6 | \n",
+ " 3.1 | \n",
+ " 1.5 | \n",
+ " 0.2 | \n",
+ " 1.483871 | \n",
+ " 7.5 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 5.0 | \n",
+ " 3.6 | \n",
+ " 1.4 | \n",
+ " 0.2 | \n",
+ " 1.388889 | \n",
+ " 7.0 | \n",
+ " 0 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sepal_length sepal_width petal_length petal_width \\\n",
+ "0 5.1 3.5 1.4 0.2 \n",
+ "1 4.9 3.0 1.4 0.2 \n",
+ "2 4.7 3.2 1.3 0.2 \n",
+ "3 4.6 3.1 1.5 0.2 \n",
+ "4 5.0 3.6 1.4 0.2 \n",
+ "\n",
+ " sepal_length_to_sepal_width petal_length_to_petal_width target \n",
+ "0 1.457143 7.0 0 \n",
+ "1 1.633333 7.0 0 \n",
+ "2 1.468750 6.5 0 \n",
+ "3 1.483871 7.5 0 \n",
+ "4 1.388889 7.0 0 "
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dataset.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Save features\n",
+ "features_path = './data/processed/featured_iris.csv'\n",
+ "dataset.to_csv(features_path, index=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "LineaArtifact(name='iris-preprocessed', _version=4)"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#save features to lineapy\n",
+ "lineapy.save(dataset, \"iris-preprocessed\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Split dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:06.361378Z",
+ "start_time": "2019-06-16T21:21:06.358647Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "test_size=0.2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Splittail train/test"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:07.438133Z",
+ "start_time": "2019-06-16T21:21:07.431649Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "((120, 7), (30, 7))"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "train_dataset, test_dataset = train_test_split(dataset, test_size=test_size, random_state=42)\n",
+ "train_dataset.shape, test_dataset.shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Save train and test sets\n",
+ "trainset_path = './data/processed/train_iris.csv'\n",
+ "testset_path = './data/processed/test_iris.csv'\n",
+ "\n",
+ "train_dataset.to_csv(trainset_path)\n",
+ "test_dataset.to_csv(testset_path)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "LineaArtifact(name='test-dataset', _version=4)"
+ ]
+ },
+ "execution_count": 20,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#save train and test sets to lineapy\n",
+ "lineapy.save(train_dataset, \"train-dataset\")\n",
+ "lineapy.save(test_dataset, \"test-dataset\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Train"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:10.932148Z",
+ "start_time": "2019-06-16T21:21:10.927844Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Get X and Y\n",
+ "\n",
+ "y_train = train_dataset.loc[:, 'target'].values.astype('int32')\n",
+ "X_train = train_dataset.drop('target', axis=1).values.astype('float32')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:55.427365Z",
+ "start_time": "2019-06-16T21:21:55.416431Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "LogisticRegression(C=0.001, multi_class='multinomial')"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Create an instance of Logistic Regression Classifier CV and fit the data\n",
+ "\n",
+ "logreg = LogisticRegression(C=0.001, solver='lbfgs', multi_class='multinomial', max_iter=100)\n",
+ "logreg.fit(X_train, y_train)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['./models/model.joblib']"
+ ]
+ },
+ "execution_count": 23,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "model_path= './models/model.joblib'\n",
+ "joblib.dump(logreg, model_path)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "LineaArtifact(name='logreg-model', _version=3)"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#save model to lineapy\n",
+ "lineapy.save(model_path, \"logreg-model\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Evaluate"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:55.875303Z",
+ "start_time": "2019-06-16T21:21:55.864724Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "def plot_confusion_matrix(cm,\n",
+ " target_names,\n",
+ " title='Confusion matrix',\n",
+ " cmap=None,\n",
+ " normalize=True):\n",
+ " \"\"\"\n",
+ " given a sklearn confusion matrix (cm), make a nice plot\n",
+ "\n",
+ " Arguments\n",
+ " ---------\n",
+ " cm: confusion matrix from sklearn.metrics.confusion_matrix\n",
+ "\n",
+ " target_names: given classification classes such as [0, 1, 2]\n",
+ " the class names, for example: ['high', 'medium', 'low']\n",
+ "\n",
+ " title: the text to display at the top of the matrix\n",
+ "\n",
+ " cmap: the gradient of the values displayed from matplotlib.pyplot.cm\n",
+ " see http://matplotlib.org/examples/color/colormaps_reference.html\n",
+ " plt.get_cmap('jet') or plt.cm.Blues\n",
+ "\n",
+ " normalize: If False, plot the raw numbers\n",
+ " If True, plot the proportions\n",
+ "\n",
+ " Usage\n",
+ " -----\n",
+ " plot_confusion_matrix(cm = cm, # confusion matrix created by\n",
+ " # sklearn.metrics.confusion_matrix\n",
+ " normalize = True, # show proportions\n",
+ " target_names = y_labels_vals, # list of names of the classes\n",
+ " title = best_estimator_name) # title of graph\n",
+ "\n",
+ " Citiation\n",
+ " ---------\n",
+ " http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html\n",
+ "\n",
+ " \"\"\"\n",
+ "\n",
+ " accuracy = np.trace(cm) / float(np.sum(cm))\n",
+ " misclass = 1 - accuracy\n",
+ "\n",
+ " if cmap is None:\n",
+ " cmap = plt.get_cmap('Blues')\n",
+ "\n",
+ " plt.figure(figsize=(8, 6))\n",
+ " plt.imshow(cm, interpolation='nearest', cmap=cmap)\n",
+ " plt.title(title)\n",
+ " plt.colorbar()\n",
+ "\n",
+ " if target_names is not None:\n",
+ " tick_marks = np.arange(len(target_names))\n",
+ " plt.xticks(tick_marks, target_names, rotation=45)\n",
+ " plt.yticks(tick_marks, target_names)\n",
+ "\n",
+ " if normalize:\n",
+ " cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n",
+ "\n",
+ " thresh = cm.max() / 1.5 if normalize else cm.max() / 2\n",
+ " for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n",
+ " if normalize:\n",
+ " plt.text(j, i, \"{:0.4f}\".format(cm[i, j]),\n",
+ " horizontalalignment=\"center\",\n",
+ " color=\"white\" if cm[i, j] > thresh else \"black\")\n",
+ " else:\n",
+ " plt.text(j, i, \"{:,}\".format(cm[i, j]),\n",
+ " horizontalalignment=\"center\",\n",
+ " color=\"white\" if cm[i, j] > thresh else \"black\")\n",
+ "\n",
+ " plt.tight_layout()\n",
+ " plt.ylabel('True label')\n",
+ " plt.xlabel('Predicted label\\naccuracy={:0.4f}; misclass={:0.4f}'.format(accuracy, misclass))\n",
+ " \n",
+ " return plt.gcf()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:56.090756Z",
+ "start_time": "2019-06-16T21:21:56.086966Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Get X and Y\n",
+ "\n",
+ "y_test = test_dataset.loc[:, 'target'].values.astype('int32')\n",
+ "X_test = test_dataset.drop('target', axis=1).values.astype('float32')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:56.270245Z",
+ "start_time": "2019-06-16T21:21:56.265054Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "prediction = logreg.predict(X_test)\n",
+ "cm = confusion_matrix(prediction, y_test)\n",
+ "f1 = f1_score(y_true = y_test, y_pred = prediction, average='macro')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2019-06-16T21:21:56.493617Z",
+ "start_time": "2019-06-16T21:21:56.489929Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.9305555555555555"
+ ]
+ },
+ "execution_count": 35,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# f1 score value\n",
+ "f1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Save metrics\n",
+ "metrics_file = './reports/metrics.json'\n",
+ "\n",
+ "metrics = {\n",
+ " 'f1': f1\n",
+ "}\n",
+ "\n",
+ "with open(metrics_file, 'w') as mf:\n",
+ " json.dump(\n",
+ " obj=metrics,\n",
+ " fp=mf,\n",
+ " indent=4\n",
+ " )\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "cm_plot = plot_confusion_matrix(cm, data.target_names, normalize=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Save confusion matrix image\n",
+ "confusion_matrix_image = './reports/confusion_matrix.png'\n",
+ "cm_plot.savefig(confusion_matrix_image)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "LineaArtifact(name='plot-confusion-matrix', _version=1)"
+ ]
+ },
+ "execution_count": 40,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "#save confusion matrix to lineapy\n",
+ "lineapy.save(plot_confusion_matrix, \"plot-confusion-matrix\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#commenting for change\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.5"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": true,
+ "sideBar": true,
+ "skip_h1_title": false,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": false,
+ "toc_position": {},
+ "toc_section_display": true,
+ "toc_window_display": true
+ },
+ "varInspector": {
+ "cols": {
+ "lenName": 16,
+ "lenType": 16,
+ "lenVar": 40
+ },
+ "kernels_config": {
+ "python": {
+ "delete_cmd_postfix": "",
+ "delete_cmd_prefix": "del ",
+ "library": "var_list.py",
+ "varRefreshCmd": "print(var_dic_list())"
+ },
+ "r": {
+ "delete_cmd_postfix": ") ",
+ "delete_cmd_prefix": "rm(",
+ "library": "var_list.r",
+ "varRefreshCmd": "cat(var_dic_list()) "
+ }
+ },
+ "types_to_exclude": [
+ "module",
+ "function",
+ "builtin_function_or_method",
+ "instance",
+ "_Feature"
+ ],
+ "window_display": false
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/requirements.txt b/requirements.txt
index d5b4910e..95cfe9b2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,13 +1,13 @@
-dvc==2.6.4
-joblib==1.0.1
-jupyter==1.0.0
-jupyter_contrib_nbextensions==0.5.1
-matplotlib==3.4.3
-numpy==1.21.2
-pandas==1.3.2
-pytest==6.2.4
-python-box==5.4.1
-pyyaml==5.4.1
-scikit-learn==0.24.2
-scipy==1.7.1
-tqdm==4.62.2
\ No newline at end of file
+dvc>=2.8.3,<3
+joblib>=1.0.1,<2
+jupyter>=1.0.0,<2
+jupyter_contrib_nbextensions>=0.5.1,<1
+matplotlib>=3.4.3,<4
+numpy>=1.21.2,<2
+pandas>=1.3.2,<2
+pytest>=6.2.4,<7
+python-box>=5.4.1,<6
+pyyaml>=5.4.1,<6
+scikit-learn>=0.24.2,<2
+scipy>=1.7.1,<2
+tqdm>=4.62.2,<5
diff --git a/step-0-prototype.ipynb b/step-0-prototype.ipynb
index 3f7fee6c..608a10f2 100644
--- a/step-0-prototype.ipynb
+++ b/step-0-prototype.ipynb
@@ -383,7 +383,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.8.10"
+ "version": "3.9.2"
},
"toc": {
"base_numbering": 1,