{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "(03:how-to-package-a-python)=\n", "# How to package a Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To start this book, we will first develop an entire Python package from beginning to end. The aim of this chapter is to give the reader a simple and high level overview of the key steps involved in developing a Python package and what the final product we're building actually looks like. Later chapters will then explore each of these key steps in more detail. This chapter was inspired by [The Whole Game chapter](https://r-pkgs.org/whole-game.html) of the [R packages book](https://r-pkgs.org/) written by Hadley Wickham and Jenny Bryan." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Package structure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The first thing to do is create a directory structure for our Python package. Without getting too technical, a Python package is just a structured of one or more modules. So that we don't have to create this structure from scratch, we will use Cookiecutter & Poetry to do this for us (both of which you installed back in {ref}`02:system-setup`).\n", "\n", "Cookiecutter is a tool for populating a file and directory structure from a pre-made template. We have developed our own [Cookiecutter template](https://github.com/UBC-MDS/cookiecutter-ubc-mds) which is based off the template by the [PyOpenSci](https://www.pyopensci.org/) organization for creating Python packages (PyOpenSci is a not-for-profit organization that promotes open and reproducible research through peer-review of scientific Python packages). To use our Cookiecutter template to set up the structure of your Python package, open up a terminal session, change into the directory where you want your package to live and run the line of code below:\n", "\n", "```bash\n", "cookiecutter https://github.com/UBC-MDS/cookiecutter-ubc-mds.git\n", "```\n", "\n", "You will be prompted to provide information that will help customize the project.\n", "\n", "```{note}\n", "In this tutorial we will be calling our package `pypkgs`. However, we will eventually be publishing our package to Python's main package index [PyPI](https://pypi.org/). Package names on PyPI must be unique. As a result, you will need to choose a **unique name for your package** while following this tutorial. Something like `pypkgs_[your intials]` might be appropriate (e.g., `pypkgs_TB`). You should always check if your chosen name is already taken by visiting PyPI and searching for that name.\n", "```\n", "\n", "Below is an example of how to respond to the Cookiecutter prompts (default values for each attribute are shown in square brackets, hitting enter will accept the default attribute value):\n", "\n", "```bash\n", "full_name [Monty Python]: Tomas Beuzen\n", "github_username [mpython]: TomasBeuzen\n", "project_name [My Python package]: pypkgs\n", "project_slug [pypkgs]: \n", "project_short_description [This cookiecutter creates a boilerplate for a Python project.]: Python package that eases the pain of concatenating Pandas categoricals!\n", "version ['0.1.0']: \n", "Select open_source_license:\n", "1 - MIT license\n", "2 - BSD license\n", "3 - ISC license\n", "4 - Apache Software License 2.0\n", "5 - GNU General Public License v3\n", "Choose from 1, 2, 3, 4, 5 [1]: \n", "Select include_github_actions:\n", "1 - no\n", "2 - build\n", "3 - build+deploy\n", "Choose from 1, 2, 3 [1]:\n", "```\n", "\n", "```{attention}\n", "In the example above we chose not to include any GitHub Actions workflows in our initial directory structure. GitHub Actions workflows can help automate the building, testing and deployment of your Python package, workflows typically called Continuous Integration (CI) and Continuous Deployment (CD). We'll explore these topics in more detail in a later chapter.\n", "```\n", "\n", "We now have a new directory called `pypkgs` (of course, your package will have a different name but for the rest of the book, we'll keep referring to it as `pypkgs`). Next, we need to navigate into the `pypkgs` directory and initialize the project as a Poetry project so that we can take advantage of the package management and building tools of Poetry:\n", "\n", "```bash\n", "cd pypkgs\n", "poetry init\n", "```\n", "\n", "Again we are prompted for more information related to our package. Once again, default values are shown in square brackets and have been populated where possible from the Cookiecutter template. Here is an example of how to respond to the prompts:\n", "\n", "```bash\n", "This command will guide you through creating your pyproject.toml config.\n", "\n", "Package name [pypkgs]: \n", "Version [0.1.0]: \n", "Description []: Python package that eases the pain of concatenating Pandas categoricals!\n", "Author [Tomas Beuzen <tomas.beuzen@gmail.com>, n to skip]: \n", "License []: MIT\n", "Compatible Python versions [^3.7]: \n", "\n", "Would you like to define your main dependencies interactively? (yes/no) [yes] no\n", "Would you like to define your development dependencies interactively? (yes/no) [yes] no\n", "Generated file\n", "\n", "[tool.poetry]\n", "name = \"pypkgs\"\n", "version = \"0.1.0\"\n", "description = \"Python package that eases the pain of concatenating Pandas categoricals!\"\n", "authors = [\"Tomas Beuzen <tomas.beuzen@gmail.com>\"]\n", "license = \"MIT\"\n", "\n", "[tool.poetry.dependencies]\n", "python = \"^3.7\"\n", "\n", "[tool.poetry.dev-dependencies]\n", "\n", "[build-system]\n", "requires = [\"poetry>=0.12\"]\n", "build-backend = \"poetry.masonry.api\"\n", "\n", "\n", "Do you confirm generation? (yes/no) [yes] \n", "```\n", "\n", "```{note}\n", "We said \"no\" to defining our dependencies interactively because it is more efficient to define them using `poetry add` which we will explore a bit later on.\n", "```\n", "\n", "After using Cookiecutter and Poetry, we end up with the following directory structure:\n", "\n", "```bash\n", "pypkgs\n", "├── CONDUCT.rst\n", "├── CONTRIBUTING.rst\n", "├── CONTRIBUTORS.rst\n", "├── docs\n", "│ ├── conduct.rst\n", "│ ├── conf.py\n", "│ ├── contributing.rst\n", "│ ├── contributors.rst\n", "│ ├── index.rst\n", "│ ├── installation.rst\n", "│ ├── make.bat\n", "│ ├── Makefile\n", "│ └── usage.rst\n", "├── pypkgs\n", "│ ├── __init__.py\n", "│ └── pypkgs.py\n", "├── .gitignore\n", "├── LICENSE\n", "├── pyproject.toml\n", "├── .readthedocs.yml\n", "├── README.md\n", "└── tests\n", " ├── __init__.py\n", " └── test_pypkgs.py\n", "```\n", "\n", "These two simple steps (Cookiecutter + Poetry) have given us a boilerplate file and directory structure suitable for building our Python package. While there are quite a few files in our boilerplate, at this point we only need to worry about a few of these to get a working package together. Specifically, we'll be working on:\n", "\n", "- the file where we will write the Python functions that our package will distribute (`pypkgs/pypkgs.py`);\n", "- the file where we will write tests to ensure that our package's functions work as we expect (`tests/test_pypkgs.py`); and,\n", "- the `pyproject.toml` file that defines our project's metadata and dependencies and how it will eventually be built and distributed.\n", "\n", "Later chapters will focus on the other components of the boilerplate, which can be used to refine your package and packaging process with, for example, quality documentation, extensive testing, continuous integration, version bumping, continuous deployment, etc.\n", "\n", "```{tip}\n", "Users of the RStudio IDE may also want to make this Python project directory an RStudio project. Why? Well, once you have an `*.Rproj` file, you can use that file to quickly open the RStudio IDE (which has a terminal and an interactive Python REPL, assuming you have set this up with `reticulate`) to the project's root directory.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Putting your project under version control" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before we start developing our package it is generally good practice to put your data science projects under local and remote version control. The tools we recommend using for this are Git & GitHub. For this book, we assume readers have [Git](https://git-scm.com/) installed on their machine, have novice Git skills, and have a [GitHub.com](https://github.com/) account." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Set up local version control" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the terminal and in the root `pypkgs` directory, we will initialize the repository to be tracked by Git using:\n", "\n", "```bash\n", "git init\n", "\n", "Initialized empty Git repository in /Users/tbeuzen/GitHub/py-pkgs/pypkgs/.git/\n", "```\n", "\n", "Next, we need to tell Git which files to track (which will be all of them at this point) and commit these changes locally:\n", "\n", "```bash\n", "git add .\n", "git commit -m \"initial package setup\"\n", "[master (root-commit) 8b4edcb] initial package setup\n", " 21 files changed, 704 insertions(+)\n", " create mode 100644 .gitignore\n", " create mode 100644 .readthedocs.yml\n", " create mode 100755 CONDUCT.rst\n", " ...\n", " create mode 100644 pyproject.toml\n", " create mode 100644 tests/__init__.py\n", " create mode 100644 tests/test_pypkgs.py\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Set up remote version control" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have set up our local version control, let's create a repository on [GitHub.com](https://github.com/) and set that as the remote version control home for this project:\n", "\n", "```{figure} images/set-up-github-1.png\n", "---\n", "width: 600px\n", "name: github-1\n", "---\n", "Creating a new repository in GitHub.\n", "```\n", "\n", "The options we recommend for setting up a repository for a Python package using the workflow we present in this book include: \n", "\n", "- give the GitHub.com repository the same name as your Python Poetry project's name;\n", "- make the GitHub.com repository public; and,\n", "- **do not** initialize the GitHub.com repository with a README file.\n", "\n", "```{figure} images/set-up-github-2.png\n", "---\n", "width: 600px\n", "name: github-2\n", "---\n", "Setting up a new repository in GitHub.\n", "```\n", "\n", "Next, copy the remote link to your repository and use the following commands to set the remote address locally, and push your project to GitHub.com:\n", "\n", "```bash\n", "git remote add origin git@github.com:TomasBeuzen/pypkgs.git\n", "git push -u origin master\n", "Enumerating objects: 25, done.\n", "Counting objects: 100% (25/25), done.\n", "Delta compression using up to 8 threads\n", "Compressing objects: 100% (20/20), done.\n", "Writing objects: 100% (25/25), 10.17 KiB | 2.03 MiB/s, done.\n", "Total 25 (delta 0), reused 0 (delta 0)\n", "To github.com:TomasBeuzen/pypkgs.git\n", " * [new branch] master -> master\n", "Branch 'master' set up to track remote branch 'master' from 'origin'.\n", "```\n", "\n", "```{note}\n", "The example above uses SSH authentication with GitHub. SSH is useful for connecting to GitHub without having to supply your username and password every time. If you're interested in setting up SSH, take a look at the [GitHub documentation](https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh). If you don't have SSH authentication set up, HTTPS authentication works as well and would require the use of this url in place of the one shown above to set the remote: `https://github.com/TomasBeuzen/pypkgs.git`. \n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Writing your first function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Pandas categoricals](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Categorical.html) are a very useful data type for modeling (and were inspired by \"factors\" in R), but certain manipulations of this data type can be tricky during data wrangling. One such manipulation is the concatenation (joining) of two Pandas categoricals. Let's observe the result of trying to concatenate two Pandas categorical objects:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "tags": [ "raises-exception" ] }, "outputs": [ { "ename": "TypeError", "evalue": "cannot concatenate object of type \"<class 'pandas.core.arrays.categorical.Categorical'>\"; only pd.Series, pd.DataFrame, and pd.Panel (deprecated) objs are valid", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m<ipython-input-1-1af7dac6ee60>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCategorical\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"character\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"hits\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"your\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"eyeballs\"\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 3\u001b[0m \u001b[0mb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCategorical\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"but\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"integer\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"where it\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"counts\"\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----> 4\u001b[0;31m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconcat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\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[0m", "\u001b[0;32m/opt/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/concat.py\u001b[0m in \u001b[0;36mconcat\u001b[0;34m(objs, axis, join, join_axes, ignore_index, keys, levels, names, verify_integrity, sort, copy)\u001b[0m\n\u001b[1;32m 226\u001b[0m \u001b[0mkeys\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mkeys\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlevels\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mlevels\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnames\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mnames\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 227\u001b[0m \u001b[0mverify_integrity\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mverify_integrity\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 228\u001b[0;31m copy=copy, sort=sort)\n\u001b[0m\u001b[1;32m 229\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_result\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 230\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/opt/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/concat.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, objs, axis, join, join_axes, keys, levels, names, ignore_index, verify_integrity, copy, sort)\u001b[0m\n\u001b[1;32m 287\u001b[0m \u001b[0;34m' only pd.Series, pd.DataFrame, and pd.Panel'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 288\u001b[0m ' (deprecated) objs are valid'.format(type(obj)))\n\u001b[0;32m--> 289\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\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 290\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 291\u001b[0m \u001b[0;31m# consolidate\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: cannot concatenate object of type \"<class 'pandas.core.arrays.categorical.Categorical'>\"; only pd.Series, pd.DataFrame, and pd.Panel (deprecated) objs are valid" ] } ], "source": [ "import pandas as pd\n", "a = pd.Categorical([\"character\", \"hits\", \"your\", \"eyeballs\"])\n", "b = pd.Categorical([\"but\", \"integer\", \"where it\", \"counts\"])\n", "pd.concat([a, b])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{tip}\n", "Pandas comes packaged with the Anaconda distribution we installed in {ref}`02:system-setup`. However, if for some reason you don't currently have Pandas installed, you can install it with `pip` or `conda`, check out the official [Pandas documentation](https://pandas.pydata.org/docs/getting_started/install.html).\n", "```\n", "\n", "This error occurs because the categoricals are represented as integers in memory, and in the variable `a`, the integer 0 corresponds to the word \"character\" while in `b`, the integer 0 corresponds to the word \"but\". Thus, when we ask Python to concatenate these two Pandas categorical options it doesn't know what to do with these integer mappings to different categories, and so it throws an error. We can get around this several ways, one way is to convert the Pandas categoricals to a `str` type, then do the concatenation, and finally convert the concatenated Pandas obeject back to a categorical again. We demonstrate that approach below:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[character, hits, your, eyeballs, but, integer, where it, counts]\n", "Categories (8, object): [but, character, counts, eyeballs, hits, integer, where it, your]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "concatenated = pd.concat([pd.Series(a.astype(\"str\")), pd.Series(b.astype(\"str\"))])\n", "pd.Categorical(concatenated)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That seems to work 🎉 , but it's quite a bit of typing every time we want to do this... So let's turn this code into a function called `catbind`!" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[character, hits, your, eyeballs, but, integer, where it, counts]\n", "Categories (8, object): [but, character, counts, eyeballs, hits, integer, where it, your]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def catbind(a, b):\n", " concatenated = pd.concat([pd.Series(a.astype(\"str\")),\n", " pd.Series(b.astype(\"str\"))])\n", " return pd.Categorical(concatenated)\n", "\n", "catbind(a, b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{note}\n", "This book assumes you know how to write, document and test functions in Python. To learn more about this see [Think Python, Chapter 3: Functions](http://greenteapress.com/thinkpython/html/thinkpython004.html) by Allen Downey.\n", "```\n", "\n", "So where do we save this function if we want it to be a part of our `pypkgs` Python package? Let's review the landscape of our Python project so far:\n", "\n", "```bash\n", "pypkgs\n", "├── CONDUCT.rst\n", "├── CONTRIBUTING.rst\n", "├── CONTRIBUTORS.rst\n", "├── docs\n", "│ └── conduct.rst\n", "│ └── conf.py\n", "│ └── contributing.rst\n", "│ └── contributors.rst\n", "│ └── index.rst\n", "│ └── installation.rst\n", "│ └── make.bat\n", "│ └── Makefile\n", "│ └── usage.rst\n", "├── pypkgs\n", "│ └── __init__.py\n", "│ └── pypkgs.py\n", "├── .gitignore\n", "├── LICENSE\n", "├── pyproject.toml\n", "├── .readthedocs.yml\n", "├── README.md\n", "└── tests\n", " ├── __init__.py\n", " └── test_pypkgs.py\n", "```\n", "\n", "All the code that we would like the user to run as part of our package should live inside the `pypkgs` directory. Typically, for a relatively small package with just a few functions, we would house them inside a single python module (i.e., a `.py` file). Our template project directory structure already created and named such a module for us: `pypkgs/pypkgs.py`. Let's save our function there. Additionally, given that our package depends on the Pandas Python package, we should import Pandas at the top of the `pypkgs.py` file. Here's what `pypkgs.py` should look like:\n", "\n", "```python\n", "import pandas as pd\n", "\n", "\n", "def catbind(a, b):\n", " concatenated = pd.concat([pd.Series(a.astype(\"str\")),\n", " pd.Series(b.astype(\"str\"))])\n", " return pd.Categorical(concatenated)\n", "\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test drive your package code" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To test drive the function we just wrote we first install our package locally using Python poetry. We choose to do this with Python Poetry as opposed to using Python's native package manager `pip` because Poetry automatically creates and activates a virtual environment for us and will perform tricky tasks like package solving that can sometimes trip us up when we use `pip` alone. We can install our package locally by ensuring we are in our root package directory and running:\n", "\n", "```bash\n", "poetry install\n", "Creating virtualenv pypkgs-7_Ony_oT-py3.7 in /Users/tbeuzen/Library/Caches/pypoetry/virtualenvs\n", "Installing dependencies from lock file\n", "\n", "No dependencies to install or update\n", "\n", " - Installing pypkgs (0.1.0)\n", "```\n", "\n", "Now, inside the root project directory we can open an interactive Python session (by typing `python` at the command line) and import our `pypkgs` module which contains our `catbind` function as shown:\n", "\n", "```python\n", "from pypkgs import pypkgs\n", "```\n", "\n", "The `pypkgs` module has now been mapped to the current session's namespace and we can access the `catbind` function in our Python session using dot notation: `pypkgs.catbind` (note that if you wanted to import just the `catbind` function, rather than the whole `pypkgs` module, you could do `from pypkgs.pypkgs import catbind`, in which case \"dot notation\" would not be required to use the function). Let's try to use the function to concatenate two Pandas categoricals:\n", "\n", "```python\n", "import pandas as pd\n", "a = pd.Categorical([\"character\", \"hits\", \"your\", \"eyeballs\"])\n", "b = pd.Categorical([\"but\", \"integer\", \"where it\", \"counts\"])\n", "pypkgs.catbind(a, b)\n", "\n", "[character, hits, your, eyeballs, but, integer, where it, counts]\n", "Categories (8, object): [but, character, counts, eyeballs, hits, integer, where it, your]\n", "```\n", "\n", "Hurray again! This seems to work as expected! Now that we have something working, you can exit your Python session (by typing `exit()`) and commit changes to version control:\n", "\n", "```bash\n", "git add .\n", "git commit -m \"First working version of catbind function\"\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Add package dependencies" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Our function depends on the Pandas package, and without it, it would fail to work. Thus we need to record this dependency in a useful place so that when we publish our packaged code this important information (and the mechanism for making it work) will be shipped along with it. We again use `poetry` to do this, using the `add` command. This command will update the `[tool.poetry.dependencies]` section of the `pyproject.toml` file which currently looks like this and lists only Python as a project dependency:\n", "\n", "```bash\n", "[tool.poetry]\n", "name = \"pypkgs\"\n", "version = \"0.1.0\"\n", "description = \"Python package that eases the pain of concatenating Pandas categoricals!\"\n", "authors = [\"Tomas Beuzen <tomas.beuzen@gmail.com>\"]\n", "license = \"MIT\"\n", "\n", "[tool.poetry.dependencies]\n", "python = \"^3.7\"\n", "\n", "[tool.poetry.dev-dependencies]\n", "\n", "[build-system]\n", "requires = [\"poetry>=0.12\"]\n", "build-backend = \"poetry.masonry.api\"\n", "```\n", "\n", "Let's add our Pandas dependency now:\n", "\n", "```bash\n", "poetry add pandas\n", "\n", "Using version ^1.0.5 for pandas\n", "\n", "Updating dependencies\n", "Resolving dependencies... (0.2s)\n", "\n", "Writing lock file\n", "\n", "\n", "Package operations: 5 installs, 0 updates, 0 removals\n", "\n", " - Installing six (1.15.0)\n", " - Installing numpy (1.19.0)\n", " - Installing python-dateutil (2.8.1)\n", " - Installing pytz (2020.1)\n", " - Installing pandas (1.0.5)\n", "```\n", "\n", "Now if we view our `pyproject.toml` file we see that `pandas` is listed as a dependency:\n", "\n", "```bash\n", "[tool.poetry]\n", "name = \"pypkgs\"\n", "version = \"0.1.0\"\n", "description = \"Python package that eases the pain of concatenating Pandas categoricals!\"\n", "authors = [\"Tomas Beuzen <tomas.beuzen@gmail.com>\"]\n", "license = \"MIT\"\n", "\n", "[tool.poetry.dependencies]\n", "python = \"^3.7\"\n", "pandas = \"^1.0.5\"\n", "\n", "[tool.poetry.dev-dependencies]\n", "\n", "[build-system]\n", "requires = [\"poetry>=0.12\"]\n", "build-backend = \"poetry.masonry.api\"\n", "```\n", "\n", "This changed two files, `pyproject.toml` (which we printed above) and `poetry.lock` (a record of all the packages and exact versions of them that poetry downloaded for this project). These changes are important for our package, so let's commit them to version control as well:\n", "\n", "```bash\n", "git add .\n", "git commit -m \"added pandas as a dependency\"\n", "```\n", "\n", "```{note}\n", "For those of you who have used `requirements.txt` before with `pip` or a `environment.yml` with `conda`, you can think of `poetry.lock` as the `poetry` equivalent of those files.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Package documentation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reading and rendering documentation locally" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the users of your code (including your future self) we need to have readable and accessible documentation expressing how to install your package, and how to use the functions within it. We'll discuss documentation in detail in the chapter {ref}`06:documentation`, but for now, we will demonstrate the basic steps required to get your documentation up-and-running quickly.\n", "\n", "The Python packaging ecosystem has a tool to help you easily make documentation - [Sphinx](https://docs.readthedocs.io/en/stable/intro/getting-started-with-sphinx.html). In the Cookiecutter template we used to define our package's directory structure, there is a basic docs template that the Cookiecutter progam filled in with the information you entered interactively when you ran `cookiecutter https://github.com/UBC-MDS/cookiecutter-ubc-mds.git`. These files live in the `docs` directory and are `.rst` (reStructuredText markup language) filetype. This is a lightweight markup language that works similar to Markdown but uses different syntax. The templates provided to you here are fairly well formatted already, so you do not have to change the `.rst` formatting, however if you are interested in doing so, you can see the [Sphinx documentation](https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html) to get started.\n", "\n", "First, we need to install `sphinx` as a development dependency using `poetry`. \n", "\n", "```bash\n", "poetry add --dev sphinx\n", "```\n", "\n", "```{note}\n", "The use of `--dev` specifies a development dependency, rather than a package function dependency. A development dependency is a package that is not required by a user to use your package, but is required for development purposes. If you look in `pyproject.toml` you will see that `sphinx` gets added under the `[tool.poetry.dev-dependencies]` section as opposed to the `[tool.poetry.dependencies]` section.\n", "```\n", "\n", "Next, to render the help documents locally from `.rst` to `.html` we need to navigate into the `docs` directory and then run the `Makefile` there, directing it to run the `html` target:\n", "\n", "```bash\n", "cd docs\n", "poetry run make html\n", "```\n", "\n", "```{note}\n", "We append `poetry run` in front of most of our commands in this Python package workflow to ensure our commands are executed within our project's virtualenv and are using only the software tools we have specifically installed in that virtual environment.\n", "```\n", "\n", "```{warning}\n", "You may see some red warnings while your docs are rendering, but these can be ignored and are typically just suggestions on how to improve your docs if you wish.\n", "```\n", "\n", "If we now look inside our `docs` directory we see that it has expanded, and the rendered `.html` files live in `_build/html`. We can open `_build/html/index.html` to view our docs locally on our laptop, they should look something like this:\n", "\n", "```{figure} images/documentation-1.png\n", "---\n", "width: 600px\n", "name: documentation-1\n", "---\n", "The rendered docs homepage.\n", "```\n", "\n", "If we click on the \"Module Index\" link under the heading \"Indices and tables\" at the bottom of the page we get a \"Your file was not found message\":\n", "\n", "```{figure} images/documentation-2.png\n", "---\n", "width: 600px\n", "name: documentation-2\n", "---\n", "File not found error!\n", "```\n", "\n", "This is because we haven't written any documentation for our package function. Let's do that now by adding a `NumPy`-style docstring to the `catbind` function in `pypkgs/pypkgs.py` as shown below (we'll discuss docstring style more in the chapter {ref}`06:documentation`):\n", "\n", "```python\n", "import pandas as pd\n", "\n", "\n", "def catbind(a, b):\n", " \"\"\"\n", " Concatenates two pandas categoricals.\n", "\n", " Parameters\n", " ----------\n", " a : pandas.core.arrays.categorical.Categorical\n", " A pandas categorical.\n", " b : pandas.core.arrays.categorical.Categorical\n", " A pandas categorical that you wish to concatenate to a.\n", "\n", " Returns\n", " -------\n", " pandas.core.arrays.categorical.Categorical\n", " The new concatenated pandas categorical.\n", "\n", " Examples\n", " --------\n", " >>> from pypkgs import pypkgs\n", " >>> a = pd.Categorical([\"character\", \"hits\", \"your\", \"eyeballs\"])\n", " >>> b = pd.Categorical([\"but\", \"integer\", \"where it\", \"counts\"])\n", " >>> pypkgs.catbind(a, b)\n", " [character, hits, your, eyeballs, but, integer, where it, counts]\n", " Categories (8, object): [but, character, counts,\n", " eyeballs, hits, integer, where it, your]\n", " \"\"\"\n", " concatenated = pd.concat([pd.Series(a.astype(\"str\")),\n", " pd.Series(b.astype(\"str\"))])\n", " return pd.Categorical(concatenated)\n", "\n", "```\n", "\n", "Now we can use a `sphinx` extension (`napolean`) to render our `NumPy`-styled docstring into a modules page on our docs. To do this we need to install `napoleon` as a dev dependency:\n", "\n", "```bash\n", "poetry add --dev sphinxcontrib-napoleon\n", "```\n", "\n", "```{note}\n", "Normally to use this extension, we would also have to add `extensions = ['sphinx.ext.napoleon']` in the `conf.py` file in the `docs` directory, but we have taken care of this for you already with our Cookiecutter template.\n", "```\n", "\n", "Now we can change back to our root `pypkgs` directory, and use `sphinx-apidoc` and `poetry` to re-render our docs:\n", "\n", "```bash\n", "cd ..\n", "poetry run sphinx-apidoc -f -o docs/source pypkgs\n", "cd docs\n", "poetry run make html\n", "```\n", "\n", "Now when we click on the \"Module Index\" link under the heading \"Indices and tables\" we see a webpage that has a link to our module, `pypkgs.pypkgs`:\n", "\n", "```{figure} images/documentation-3.png\n", "---\n", "width: 600px\n", "name: documentation-3\n", "---\n", "The rendered docs module index.\n", "```\n", "\n", "And we can click on that to see the docs for `pypkgs.pypkgs.catbind`. Which should look roughly like this:\n", "\n", "```{figure} images/documentation-4.png\n", "---\n", "width: 600px\n", "name: documentation-4\n", "---\n", "Our function documentation.\n", "```\n", "\n", "Another hurray! 🎉🎉🎉 Let's commit this to version control and push to our remote:\n", "\n", "```bash\n", "cd ..\n", "git add .\n", "git commit -m \"generated and rendered docs for local viewing\"\n", "git push\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reading and rendering documentation remotely" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To share these docs online, we need to link our GitHub repository to [Read the Docs](https://readthedocs.org/) (where we will build and host our docs remotely). To do this:\n", "\n", "1. Visit <https://readthedocs.org/> and click on \"Sign up\";\n", "2. Select \"Sign up with GitHub\";\n", "3. Click \"Import a Project\";\n", "4. Click \"Import Manually\";\n", "5. Fill in the project details by providing a package name (this must be a unique name, we've already taken \"pypkgs\" so perhaps try \"pypkgs[your initials]\"), the repository URL, and leave the rest as is. Click \"Next\"; and,\n", "6. Click \"Build version\".\n", "\n", "After following the steps above, your docs should get successfully built on [Read the Docs](https://readthedocs.org/) and you should be able to access them via the \"View Docs\" button on the build page, or from the link that Cookiecutter created for you on your repositories `README.md` file.\n", "\n", "```{note}\n", "For [Read the Docs](https://readthedocs.org/) to work with the `poetry` package workflow you need to have a `.readthedocs.yml` in the root of your Python package. We have created this for you using Cookiecutter and you can view it [here](https://github.com/UBC-MDS/cookiecutter-ubc-mds/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/.readthedocs.yml).\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Testing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have interactively taken `catbind` for a test drive, but to prove to our future self and others that our code does in fact do what it is supposed to do, let's write some formal unit tests. We'll discuss testing in detail in the {ref}`05:testing` chapter, but will go over the key steps here. In Python packages, our tests live inside the `test` directory, typically in a file called `test_<module_name>.py`, thus for this package this is `tests/test_pypkgs.py`. Let's add a unit test (as a function named `test_catbind`) for our `catbind` function there now:\n", "\n", "```python\n", "from pypkgs import __version__\n", "from pypkgs import pypkgs\n", "import pandas as pd\n", "\n", "\n", "def test_version():\n", " assert __version__ == '0.1.0'\n", "\n", "def test_catbind():\n", " a = pd.Categorical([\"character\", \"hits\", \"your\", \"eyeballs\"])\n", " b = pd.Categorical([\"but\", \"integer\", \"where it\", \"counts\"])\n", " assert ((pypkgs.catbind(a, b)).codes == [1, 4, 7, 3, 0, 5, 6, 2]).all()\n", " assert ((pypkgs.catbind(a, b)).categories == [\"but\", \"character\",\n", " \"counts\", \"eyeballs\", \"hits\", \"integer\", \"where it\", \"your\"]).all()\n", "\n", "```\n", "\n", "```{note}\n", "Given that we use `pd.Categorical` to create objects to test on, we have to import the `pandas` package at the top of our test file.\n", "```\n", "\n", "While we could test our test functions by starting a Python session, importing and running them it is much more efficient to automate the testing workflow. In the Python package ecosystem one way we can do this is to use `pytest`. A single call to `pytest` from the root of a project will look for all files in the `tests` directory, import all files prefixed with `test*` and then call all functions prefixed with `test*`. Pretty great! \n", "\n", "To try this out, we first add `pytest` as a dev dependency via `poetry`:\n", "\n", "```bash\n", "poetry add --dev pytest\n", "```\n", "\n", "Then to run the tests, we use:\n", "\n", "```bash\n", "poetry run pytest\n", "\n", "============================= test session starts ==============================\n", "platform darwin -- Python 3.7.6, pytest-5.4.3, py-1.9.0, pluggy-0.13.1\n", "rootdir: /Users/tbeuzen/GitHub/py-pkgs/pypkgs\n", "collected 2 items \n", "\n", "tests/test_pypkgs.py .. [100%]\n", "\n", "============================== 2 passed in 0.56s ===============================\n", "```\n", "\n", "We get no error returned to us, indicating that our tests passed, Hurray! This suggests that the code we wrote is correct (at least to our test specifications)! Now we can share this with the world by putting these under local and remote version control:\n", "\n", "```bash\n", "git add .\n", "git commit -m \"added unit tests for catbind\"\n", "git push\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building and publishing your package" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### TestPyPI" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python packages are generally shared via the [PyPI package index](https://pypi.org/). However, when we are just starting to develop packages, and/or at the development stage of our package, we typically first check that everything works by submitting to [testPyPi](https://test.pypi.org/). `poetry` has a command called `publish` which we can use to do this, however the default behaviour is to publish to PyPI. So we need to add testPyPI to the list of repositories `poetry` knows about via:\n", "\n", "```bash\n", "poetry config repositories.test-pypi https://test.pypi.org/legacy/\n", "```\n", "\n", "Before we send our package, we first need to build it to source and wheel distributions (the format that PyPI distributes and something you'll learn more about in the next chapter {ref}`04:package-structure-and-state`) using `poetry build`:\n", "\n", "```bash\n", "poetry build\n", "\n", "Building pypkgs (0.1.0)\n", " - Building sdist\n", " - Built pypkgs-0.1.0.tar.gz\n", "\n", " - Building wheel\n", " - Built pypkgs-0.1.0-py3-none-any.whl\n", "```\n", "\n", "Finally, to publish to testPyPI we can use `poetry publish` (you will be prompted for your testPyPI username and password, sign up for one if you have not already done so):\n", "\n", "```bash\n", "poetry publish -r test-pypi\n", "```\n", "\n", "Now you should be able to visit your package on testPyPI (e.g., <https://test.pypi.org/project/pypkgs/>) and download it from there using `pip` via:\n", "\n", "```bash\n", "pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple pypkgs\n", "```\n", "\n", "```{note}\n", "By default `pip install` will search PyPI for the named package. However, we want to search testPyPI because that is where we uploaded our package. The argument `--index-url` points `pip` to the testPyPI index. However, our package `pypkgs` depends on `pandas` which can't be found on testPyPI (it is hosted on PyPI). So, we need to use the `--extra-index-url` argument to also point `pip` to PyPI so that it can pull any necessary dependencies of `pypkgs` from there.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PyPI" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When you're at the point where you're happy to officially share your package with the world, you can publish to PyPI by simply typing:\n", "\n", "```bash\n", "poetry publish\n", "```\n", "\n", "Your package will then be available on PyPI (e.g., <https://pypi.org/project/pypkgs/>) and can be installed with `pip`:\n", "\n", "```bash\n", "pip install pypkgs\n", "```\n", "\n", "````{tip}\n", "There are a number of optional arguments you can specify in your `pyproject.toml` file to control the metadata of your package, check them out in the [`poetry` documentation](https://python-poetry.org/docs/pyproject/). For example, you can use your `README.md` file as the description of your package on testPyPI or PyPI. To do this, you need to add the `readme` argument to the `[tool.poetry]` section of your `pyproject.toml` file and point to your `README.md` file, for example:\n", "\n", "```\n", "[tool.poetry]\n", "name = \"pypkgs\"\n", "version = \"0.1.0\"\n", "description = \"Python package that eases the pain of concatenating Pandas categoricals!\"\n", "authors = [\"Tomas Beuzen <tomas.beuzen@gmail.com>\"]\n", "license = \"MIT\"\n", "readme = \"README.md\"\n", "```\n", "\n", "````" ] } ], "metadata": { "celltoolbar": "Tags", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.8" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false }, "toc-autonumbering": false, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "06e8b80b569b4ac2bb5a989af9695ced": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "07d43b717dd94d33a8eeae066f36d839": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "09294cf0fd6549c78d0f15acf20400b2": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "096724e20b094296bb243e553b2820e6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_d2ef8aa5fad04047a98f733f89624812", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_d3343e0c83844ad987beb592199dd6f6", "value": -0.1 } }, "0b412fd29e8a44f5bfaaa9a7122a33c3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_1da474a760df45c3a290bbf3ba8a7f94", "IPY_MODEL_acc0b478d5b748c29c4683cbcfc8e0f3" ], "layout": "IPY_MODEL_9a4a74191d4b483f8e6ca535c155a33f" } }, "107d8e3c4e4249429f4ea5f86f97caa7": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "1455d02e130148c4a24a0943765db04a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1574978c1a56499dbe842cb3bf5b9e8f": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "16770b98026442368338a8d27ffceab4": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1989b160147948d4a6414de3b6c60694": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1a1f847a77d44da7977d6ed1ae70ac12": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_884172dbe7194ea7aba24ace80d35049", "IPY_MODEL_384a6bfb62454f0bb09a41d14a5eb9c9", "IPY_MODEL_8cc71f1ee02d4a10acbda0a6f9cbc4fb" ], "layout": "IPY_MODEL_da554c291c9f4442991fcf6d620e017e" } }, "1cb1287d7a5940829cb770a5553a95b3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "1da474a760df45c3a290bbf3ba8a7f94": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_323357ab97fc4ed0af4ab5743aa038cc", "style": "IPY_MODEL_64e2b864e41244388bcb173e865939f8" } }, "1ecb0c5110084d99bd02c6845b36b5f5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1ff35f5f7c9543158cd8fcb2fe45e951": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "200d9095bbf14c67af4083eebe042331": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_ce130e9bb1874109b6e9641a6f6b872a", "IPY_MODEL_544ffe02934f47abbe60aa68b1552268" ], "layout": "IPY_MODEL_2949d658a5e24ae8af8b48f1c68ca710" } }, "204835ba5cf741febeb96117f71f03db": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_e788ecb6fbad4de6aac05557dcf109f5", "style": "IPY_MODEL_871a273a718c4b7ab105f4bf6f9500e1" } }, "20f7461031e44d41bd5b14f4cdd8542a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "24102f82e9cf49aca85e512f59a1db81": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "262d0a636bf64c67a8b3b7fdb6852569": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "2844ba0a05594aeeb871eaf4fae2c4b9": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "28a33b60ad9947cebcab993a3ecd7b01": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_1989b160147948d4a6414de3b6c60694", "style": "IPY_MODEL_f24ec537d93846e48f35cf5bca83e11d", "value": 40 } }, "2949d658a5e24ae8af8b48f1c68ca710": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "2ad8e9ec7f1d4f5f89fadb89c51892cd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_3679e8d246954e7295c526cec653232b", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_455179b0a10a4d3793163b6270e8c1d4" } }, "2be4850df84041778088cca110e64d11": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_3b84503a8368455bac727f95db5d01d0", "IPY_MODEL_5ed1935a5446410aa7103f97d2500695" ], "layout": "IPY_MODEL_5bc7f1fcf97f41fea3a8623cf73e21a3" } }, "2ec147d796d24e06a192384ff4f271d8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_1ecb0c5110084d99bd02c6845b36b5f5", "style": "IPY_MODEL_750974cf708b48bcac5c4b3e178f9e34" } }, "323357ab97fc4ed0af4ab5743aa038cc": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "3679e8d246954e7295c526cec653232b": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "373d595c629d4c96ab6c69cabadfbae3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "384a6bfb62454f0bb09a41d14a5eb9c9": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_79023cd4f95e412d86f33dacb04f9cce", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_da243475d71a41f48bf4855c01052769" } }, "3966b0590b414fe1873b43e1835dbc0b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_096724e20b094296bb243e553b2820e6", "IPY_MODEL_890cbfc7c0f343d2b7991c18b88b8177", "IPY_MODEL_fcc376497f9d4b8aa11a102ebc19b6ab" ], "layout": "IPY_MODEL_ae6272a2743a4752b8798fed0b5dc332" } }, "39af2f7e78ff42679117a13c8e3e0c02": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_43d437e0efff40b0b7c59c66b9b815f5", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "3ac4ddb748c447f9903d248db1422cf1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_b8aa32ebeb924971ade818f6af335eec", "IPY_MODEL_e6a05e8e4f6a4cefb61f5f99b1eb13c2" ], "layout": "IPY_MODEL_e57657510026435496e5903d5c875cbe" } }, "3b84503a8368455bac727f95db5d01d0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_ce130e9bb1874109b6e9641a6f6b872a", "IPY_MODEL_544ffe02934f47abbe60aa68b1552268" ], "layout": "IPY_MODEL_20f7461031e44d41bd5b14f4cdd8542a" } }, "3ccc38f44df249cd833536b7a8a34327": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "4007d719e1aa45349acb4e9f80f0e4ba": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_2ec147d796d24e06a192384ff4f271d8", "IPY_MODEL_f285911bbe39497a88f0994509363fbb" ], "layout": "IPY_MODEL_9ecdf012164d45aaa03c28b34bebf7fa" } }, "42727846507043a098dc2e932813ee65": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_16770b98026442368338a8d27ffceab4", "style": "IPY_MODEL_b1e4ec6cae8d454999896d5ec41be03c" } }, "43d437e0efff40b0b7c59c66b9b815f5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "454c456031894c1498a69aaa4dcb0e97": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "455179b0a10a4d3793163b6270e8c1d4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "479edca2ec334d01b8af52d21f4c1565": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "494640e15fe7442ebfa3b6786ef64d82": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "4af9e16a11ed4597bbd81a734add4f5c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_535d254dd54d41ada990b62be72d5a14", "IPY_MODEL_e6a05e8e4f6a4cefb61f5f99b1eb13c2" ], "layout": "IPY_MODEL_7415264924174df19d8822f16c379f55" } }, "4c8b0dc16ddc40af8b131803f5280311": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_edcfad1763d44bbab957f8d4f981f4e4", "IPY_MODEL_d11a21b0488f4970babc1c7ad3f86aa8" ], "layout": "IPY_MODEL_ef77db303b044fe783f80b51f045e866" } }, "4d8487b20ef9419aab03dcc0c813fc77": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_cf422506005a43978b50beb43766a819", "IPY_MODEL_78efe79186ed46c78e9d91400f519f1d" ], "layout": "IPY_MODEL_ce3b1869747b4502b90f4448204f5d94" } }, "4f613b87c7184b80bbc2806c184eb4cb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_9053f57dacfc4255ad1dc0b717cbd4be", "IPY_MODEL_42727846507043a098dc2e932813ee65" ], "layout": "IPY_MODEL_8c5220ab1fe748d0abf46067668fd9d1" } }, "50f2a1ea21a24ed9a0db566a4678e24e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_933e89a135cd42e196abb362e0302570", "style": "IPY_MODEL_b4ed787903d4445cb7fd4c6827acf9cd" } }, "527235c0249e4be68af5cc1b5f6ecaa8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "535d254dd54d41ada990b62be72d5a14": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_50f2a1ea21a24ed9a0db566a4678e24e", "IPY_MODEL_bfe8c68e54b24762adb625e2d926bc2e" ], "layout": "IPY_MODEL_d9616e978f2840609e88757e2e7e47f3" } }, "544ffe02934f47abbe60aa68b1552268": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_7acc7084e5c34c5a9b18b84c2a78660b", "style": "IPY_MODEL_d10b5afbdea647baa88b2156fda8c6c0" } }, "54bac03c012745098c9455c5a2325e0a": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_1cb1287d7a5940829cb770a5553a95b3", "outputs": [ { "data": { "image/png": "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\n", "text/plain": "<Figure size 432x288 with 1 Axes>" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "57c1af3db3d44cc7bfcfc97614bea646": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_9053341eacf94d139041e6aa7cc93233", "style": "IPY_MODEL_527235c0249e4be68af5cc1b5f6ecaa8", "value": 40 } }, "587b16eff8dc4011bcc72b4f4070b14d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "58a162ea4105481e9d1345fdc79c9f1d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "5ab9a789ce9a4e97845523051ef0762c": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_965ef0e308be48eaa8b8ef19c63bed40", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "5bc7f1fcf97f41fea3a8623cf73e21a3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "5d02d239659c496cbb1972806465cbba": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "5d68805b72bd49498579e5c204071464": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_1da474a760df45c3a290bbf3ba8a7f94", "IPY_MODEL_acc0b478d5b748c29c4683cbcfc8e0f3" ], "layout": "IPY_MODEL_72b02467571a402da6d4414c5e4489fa" } }, "5e5877766afd49fcbb63cdbbff1f5684": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_1ff35f5f7c9543158cd8fcb2fe45e951", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_7bc761c122284f8b9822ff186e0f78bd", "value": -1 } }, "5ed1935a5446410aa7103f97d2500695": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_262d0a636bf64c67a8b3b7fdb6852569", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "645724dd1b174799b0ff40a9e6d6ab62": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_98b7be5be6ad4194bafdff2ffd9a89f0", "IPY_MODEL_e5aa35e4f4aa46cf8044c66339de4b96" ], "layout": "IPY_MODEL_454c456031894c1498a69aaa4dcb0e97" } }, "64c7a0f8b8904b1ca743bc4911a341b2": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "64e2b864e41244388bcb173e865939f8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "6991e87f25be44a78f759d7fd412588b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_2ad8e9ec7f1d4f5f89fadb89c51892cd", "IPY_MODEL_5e5877766afd49fcbb63cdbbff1f5684", "IPY_MODEL_81a51a474122457fa2f9da64776cf5d1" ], "layout": "IPY_MODEL_8a2b88d881b247d08b03cb1b7183128d" } }, "6a03b9b6618c43bb80bcccfc6f7c19f9": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "6b15b70b85584ce18f7b6efe71563f96": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "6c890acf62af426cab22ed65ff429088": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "6ffdc8fcab164aedbf54cf3a45384d64": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "72b02467571a402da6d4414c5e4489fa": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7415264924174df19d8822f16c379f55": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "750974cf708b48bcac5c4b3e178f9e34": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "78efe79186ed46c78e9d91400f519f1d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_fdfc64c80c3c407bacf0a395dede228c", "style": "IPY_MODEL_8b9cd3fe33484cb794a9c2a4d60caa9a" } }, "79023cd4f95e412d86f33dacb04f9cce": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7a81ba3691dc47449c052dcd9070b618": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_ac55030546d54b0ca4d7e4fbf355a3c8", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_6b15b70b85584ce18f7b6efe71563f96" } }, "7acc7084e5c34c5a9b18b84c2a78660b": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7b066d9aaaa34fcda5803a72047566f0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_d6b85a8f0ec64ee491848be1b8bf8188", "style": "IPY_MODEL_3ccc38f44df249cd833536b7a8a34327", "value": 40 } }, "7bc761c122284f8b9822ff186e0f78bd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "7c23e7df356f475e994f2f3e70730747": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7d29dde0dd4046fb9717131bf06b637f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_b674d805670c4a969daa587ae7d22805", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_494640e15fe7442ebfa3b6786ef64d82", "value": -1 } }, "81a51a474122457fa2f9da64776cf5d1": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_94487d6608754721aa3f4401e77dc292", "outputs": [ { "data": { "image/png": "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\n", "text/plain": "<Figure size 432x288 with 1 Axes>" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "856e6ab0a1b14ac490277652b9beabf6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_0b412fd29e8a44f5bfaaa9a7122a33c3", "IPY_MODEL_5ab9a789ce9a4e97845523051ef0762c" ], "layout": "IPY_MODEL_f55ac2b80afd433a919cc2d7ab87b098" } }, "871a273a718c4b7ab105f4bf6f9500e1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "884172dbe7194ea7aba24ace80d35049": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_d2e7ead2f8d74c5180cbabbb30b674a8", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_373d595c629d4c96ab6c69cabadfbae3" } }, "890cbfc7c0f343d2b7991c18b88b8177": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_24102f82e9cf49aca85e512f59a1db81", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_64c7a0f8b8904b1ca743bc4911a341b2", "value": -0.5 } }, "8a2b88d881b247d08b03cb1b7183128d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8b9cd3fe33484cb794a9c2a4d60caa9a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "8c5220ab1fe748d0abf46067668fd9d1": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8cc71f1ee02d4a10acbda0a6f9cbc4fb": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_6a03b9b6618c43bb80bcccfc6f7c19f9", "outputs": [ { "data": { "image/png": "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\n", "text/plain": "<Figure size 432x288 with 1 Axes>" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "8dda8bf6c916451e91b96e56fc4d2a76": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "9053341eacf94d139041e6aa7cc93233": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "9053f57dacfc4255ad1dc0b717cbd4be": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_ef44415ff8dd456681965184a2a299e8", "style": "IPY_MODEL_cc29a198d1d44797a909ea9a68a5d484" } }, "907bd0f286754766afa0d6ba1612d301": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_4d8487b20ef9419aab03dcc0c813fc77", "IPY_MODEL_d11a21b0488f4970babc1c7ad3f86aa8" ], "layout": "IPY_MODEL_1574978c1a56499dbe842cb3bf5b9e8f" } }, "90a70b5f560f437f9bb9422a4a6f6592": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "933e89a135cd42e196abb362e0302570": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "935fac04b32e4e0b9e1f9ea118a04ee1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_b9e169b8efac4eeb990ae37db1e8de12", "max": 200, "style": "IPY_MODEL_9927296f85f248858bdc0a0abcc67e94", "value": 100 } }, "94487d6608754721aa3f4401e77dc292": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "965ef0e308be48eaa8b8ef19c63bed40": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "97c973da7b714095959a2a66d7d28c65": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "98b7be5be6ad4194bafdff2ffd9a89f0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_b12e150681c94126b68be84b0d7cf158", "IPY_MODEL_204835ba5cf741febeb96117f71f03db" ], "layout": "IPY_MODEL_7c23e7df356f475e994f2f3e70730747" } }, "9927296f85f248858bdc0a0abcc67e94": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "9a4a74191d4b483f8e6ca535c155a33f": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "9c4a507b139f440fabaa95140d4b2193": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "9ecdf012164d45aaa03c28b34bebf7fa": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "a5e552b00bc54d1991d64d1e61258563": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_7a81ba3691dc47449c052dcd9070b618", "IPY_MODEL_7d29dde0dd4046fb9717131bf06b637f", "IPY_MODEL_54bac03c012745098c9455c5a2325e0a" ], "layout": "IPY_MODEL_2844ba0a05594aeeb871eaf4fae2c4b9" } }, "ac55030546d54b0ca4d7e4fbf355a3c8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "acc0b478d5b748c29c4683cbcfc8e0f3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_c69f7b00b5a54f60908cb3d92021daf6", "style": "IPY_MODEL_479edca2ec334d01b8af52d21f4c1565" } }, "ae6272a2743a4752b8798fed0b5dc332": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b070d7bd6a794d2ba0242e4220af368f": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b12e150681c94126b68be84b0d7cf158": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_1455d02e130148c4a24a0943765db04a", "style": "IPY_MODEL_c2bcf8f8ff7b4623a8311427461ec26b" } }, "b1e4ec6cae8d454999896d5ec41be03c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "b4ed787903d4445cb7fd4c6827acf9cd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "b65b837d3b614513bef7014f6e97b8be": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_d9226ab50b4a4df9bdd6574f239a76a5", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "b674d805670c4a969daa587ae7d22805": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b8aa32ebeb924971ade818f6af335eec": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_50f2a1ea21a24ed9a0db566a4678e24e", "IPY_MODEL_bfe8c68e54b24762adb625e2d926bc2e" ], "layout": "IPY_MODEL_587b16eff8dc4011bcc72b4f4070b14d" } }, "b9e169b8efac4eeb990ae37db1e8de12": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "bba860475dc34aa295f004356f4c7e70": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_4f613b87c7184b80bbc2806c184eb4cb", "IPY_MODEL_b65b837d3b614513bef7014f6e97b8be" ], "layout": "IPY_MODEL_f128f679eeb840f88c63d5fb65e08abe" } }, "be52a81fc3e44efc8f0d70d81da55763": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_4007d719e1aa45349acb4e9f80f0e4ba", "IPY_MODEL_39af2f7e78ff42679117a13c8e3e0c02" ], "layout": "IPY_MODEL_90a70b5f560f437f9bb9422a4a6f6592" } }, "bf592509273c491c9496cc5bce017755": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_200d9095bbf14c67af4083eebe042331", "IPY_MODEL_5ed1935a5446410aa7103f97d2500695" ], "layout": "IPY_MODEL_b070d7bd6a794d2ba0242e4220af368f" } }, "bfe8c68e54b24762adb625e2d926bc2e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_09294cf0fd6549c78d0f15acf20400b2", "style": "IPY_MODEL_d5700c1140ff4a598008ed28c45f7d25" } }, "c2bcf8f8ff7b4623a8311427461ec26b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "c2cc4c190c324cc0a0ce9bf781a499eb": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c69f7b00b5a54f60908cb3d92021daf6": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "cc29a198d1d44797a909ea9a68a5d484": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "cd9593419907498e8a80fa9fb7850749": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "ce130e9bb1874109b6e9641a6f6b872a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_c2cc4c190c324cc0a0ce9bf781a499eb", "style": "IPY_MODEL_cd9593419907498e8a80fa9fb7850749" } }, "ce3b1869747b4502b90f4448204f5d94": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "cf422506005a43978b50beb43766a819": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_9c4a507b139f440fabaa95140d4b2193", "style": "IPY_MODEL_107d8e3c4e4249429f4ea5f86f97caa7" } }, "d10b5afbdea647baa88b2156fda8c6c0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "d11a21b0488f4970babc1c7ad3f86aa8": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_97c973da7b714095959a2a66d7d28c65", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "d2a772f4a9db458db7a47d77c13dd3d5": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_6ffdc8fcab164aedbf54cf3a45384d64", "style": "IPY_MODEL_e0dea1c96cc34a53938b3f0c52e09fe1", "value": 40 } }, "d2e7ead2f8d74c5180cbabbb30b674a8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d2ef8aa5fad04047a98f733f89624812": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d3343e0c83844ad987beb592199dd6f6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "d5700c1140ff4a598008ed28c45f7d25": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "d67190fd827a4b1a8ca8079df64d0a0a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d6b85a8f0ec64ee491848be1b8bf8188": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d9226ab50b4a4df9bdd6574f239a76a5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d9616e978f2840609e88757e2e7e47f3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "da243475d71a41f48bf4855c01052769": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "da554c291c9f4442991fcf6d620e017e": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "e0dea1c96cc34a53938b3f0c52e09fe1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "e57657510026435496e5903d5c875cbe": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "e5aa35e4f4aa46cf8044c66339de4b96": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_fc608307f2064139ade48955ecbaf7e9", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "e6a05e8e4f6a4cefb61f5f99b1eb13c2": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_d67190fd827a4b1a8ca8079df64d0a0a", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "e788ecb6fbad4de6aac05557dcf109f5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "ea2be2adfc72403eb1236220a37dfd36": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_5d68805b72bd49498579e5c204071464", "IPY_MODEL_5ab9a789ce9a4e97845523051ef0762c" ], "layout": "IPY_MODEL_6c890acf62af426cab22ed65ff429088" } }, "ec1db3073c594dac8b081d2ad5e072e1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_5d02d239659c496cbb1972806465cbba", "style": "IPY_MODEL_58a162ea4105481e9d1345fdc79c9f1d", "value": 40 } }, "edcfad1763d44bbab957f8d4f981f4e4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_cf422506005a43978b50beb43766a819", "IPY_MODEL_78efe79186ed46c78e9d91400f519f1d" ], "layout": "IPY_MODEL_fd89962cc1214dd7afd0ee8f6c352b76" } }, "ef44415ff8dd456681965184a2a299e8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "ef77db303b044fe783f80b51f045e866": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "f128f679eeb840f88c63d5fb65e08abe": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "f24ec537d93846e48f35cf5bca83e11d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "f285911bbe39497a88f0994509363fbb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_07d43b717dd94d33a8eeae066f36d839", "style": "IPY_MODEL_8dda8bf6c916451e91b96e56fc4d2a76" } }, "f55ac2b80afd433a919cc2d7ab87b098": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fc608307f2064139ade48955ecbaf7e9": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fcc376497f9d4b8aa11a102ebc19b6ab": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_06e8b80b569b4ac2bb5a989af9695ced", "outputs": [ { "data": { "image/png": "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\n", "text/plain": "<Figure size 432x288 with 1 Axes>" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "fd89962cc1214dd7afd0ee8f6c352b76": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fdfc64c80c3c407bacf0a395dede228c": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }