Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/tonyfast/restartable
some thoughts on restart and run all
https://github.com/tonyfast/restartable
jupyter notebook procedural-notebooks python reusability
Last synced: 5 days ago
JSON representation
some thoughts on restart and run all
- Host: GitHub
- URL: https://github.com/tonyfast/restartable
- Owner: tonyfast
- Created: 2017-11-30T07:34:57.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2017-11-30T17:25:09.000Z (about 7 years ago)
- Last Synced: 2025-01-10T15:19:00.538Z (about 1 month ago)
- Topics: jupyter, notebook, procedural-notebooks, python, reusability
- Language: Jupyter Notebook
- Homepage: http://nbviewer.jupyter.org/format/slides/github/tonyfast/restartable/blob/master/readme.ipynb
- Size: 74.2 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.ipynb
Awesome Lists containing this project
README
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Procedural Notebooks\n",
"\n",
" \n",
"_Typically_ a notebook's author will begin an idea from a blank documents in an editable state. Through cycles of __interactive__ computing an author will transform the notebook's data by adding narrative, code, and metadata. The notebook's cells are __parts__ of a __whole__ computable document described by the notebook format.\n",
"\n",
"The __interactive__ _in-memory_ editing mode is a critical, but fleeting stage in the life of a computable document. Notebooks spend most of their existence as __whole__ & __static__ files _on disk_. The __static__ state of notebooks are reusable; and for notebooks to be reusable they must be reused. \n",
"\n",
"__Procedural__ notebooks are readable and reusable literate documents that can be executed successfully in other contexts like documention, module development, or external jobs. This notebook explores the reusability of __procedural__ notebooks that \n",
"successfully _Restart and Run All_. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### This literate document can be viewed as a [notebook](http://nbviewer.jupyter.org/github/tonyfast/restartable/blob/master/readme.ipynb), , or \n",
"\n",
"[](https://mybinder.org/v2/gh/tonyfast/restartable/master?filepath=readme.ipynb)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Motivation\n",
"\n",
"__Procedural__ notebooks are inspired by [Paco Nathan](http://liber118.com/pxn/)'s [_Oriole: a new learning medium based on Jupyter + Docker_](http://nbviewer.jupyter.org/github/jupyterday-atlanta-2016/oriole_jupyterday_atl/blob/master/oriole_talk.ipynb) given at [Jupyter Day Atlanta 2016](https://jupyterday-atlanta-2016.github.io). In Paco's _unofficial_ [styleguide for authoring Jupyter notebooks](http://nbviewer.jupyter.org/github/jupyterday-atlanta-2016/oriole_jupyterday_atl/blob/master/oriole_talk.ipynb#What-we-learned-about-teaching-with-notebooks) he suggests:\n",
"\n",
"> clear all output then \"Run All\" -- or it didn't happen"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"## __Procedural notebooks__\n",
"\n",
"* ... restart and run all, or they don't. Their reusability can be tested in different contexts.\n",
"* ... change over time\n",
"* ... encapsulate cycles of [non-structured](https://en.wikipedia.org/wiki/Non-structured_programming),\n",
"[structured](https://en.wikipedia.org/wiki/Structured_programming), and [literate programming](https://en.wikipedia.org/wiki/Literate_programming) actions.\n",
"* ... can be executed in other contexts like testing, document conversion, or compute...\n",
"* ... can be tested as __parts__ in __interactive__ mode\n",
"* ... can be tested as a __whole__ in a __procedural__ mode\n",
"* ... may be used to create sophisticated software projects.\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# This notebook is procedural notebook\n",
"\n",
"Its cells _Restart and Run All_ to create a module and python package called __particles__:\n",
"\n",
"* Create, describe, and test source code for a project we call __particles__.\n",
"* Copy the source code to a notebook called __particles.ipynb__\n",
"* Convert __particles.ipynb__ to __particles.py__ and a Python package called __particles__."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" > __particles__ is inspired by the New York Times R&D [_The Future of News is Not an Article_](http://nytlabs.com/blog/2015/10/20/particles/). __particles__ treat elements of computable documents as data and modular components."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## Procedurally create the particles module\n",
"\n",
"__readme.ipynb__ generates the __particles__ module either in interactive mode, or procedurally from a converted Python script. \n",
"\n",
"`attach` is a callable used by __readme__ to append the recent `In`put as cell source to __particles.ipynb__; _it is erroneous to the __particles__ module. _If the __readme.ipynb__ cells are run out of order then __particles.ipynb__ could be created incorrectly._"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from nbformat import v4, NotebookNode\n",
"nb, particles = 'particles.ipynb', v4.new_notebook();\n",
"def attach(nb:NotebookNode=particles)->None:\n",
" \"\"\"attach an input to another notebook removing attach statements.\n",
" >>> nb = v4.new_notebook();\n",
" >>> assert attach(nb) or ('doctest' in nb.cells[-1].source)\"\"\"\n",
" 'In' in globals() and nb.cells.append(v4.new_code_cell('\\n'.join(\n",
" str for str in In[-1].splitlines() if not str.startswith('attach'))))"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting requirements.txt\n"
]
}
],
"source": [
" %%file requirements.txt\n",
" pandas\n",
" matplotlib"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"# build `particles.ipynb`\n",
"\n",
"> Many cells in __readme.ipynb__ have lived and died before you read this line.\n",
"\n",
"The __code__ cell below will be appended to __particles.ipynb__. It __import__s tools into __readme.ipynb__'s interactive mode. It now becomes quite easy to iteratively develop and test __parts__ of the procedural document."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'particles treat notebooks as data'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"attach(particles)\n",
"\"\"\"particles treat notebooks as data\"\"\""
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"attach(particles)\n",
"from nbformat import reads, v4 \n",
"from pandas import concat, DataFrame, to_datetime \n",
"from pathlib import Path "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"# callables in `particles` \n",
"\n",
"Create two main functions for __particles__ to export"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"attach()\n",
"def read_notebooks(dir:str='.')->DataFrame:\n",
" \"\"\"Read a directory of notebooks into a pandas.DataFrame\n",
" >>> df = read_notebooks('.')\n",
" >>> assert len(df) and isinstance(df, DataFrame)\"\"\"\n",
" return concat({\n",
" file: DataFrame(reads(file.read_text(), 4)['cells'])\n",
" for file in Path(dir).glob('*.ipynb')\n",
" }).reset_index(-1, drop=True)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"The `read_notebooks` index is a pathlib object containing extra metadata. `files_to_data` extracts the `stat` properties for each file."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"attach()\n",
"def files_to_data(df:DataFrame)->DataFrame:\n",
" \"\"\"Transform an index of Path's to a dataframe of os_stat.\n",
" >>> df = files_to_data(read_notebooks())\n",
" \"\"\"\n",
" stats, index = [], df.index.unique()\n",
" for file in index:\n",
" stat = file.stat() \n",
" stats.append({\n",
" key: to_datetime(\n",
" getattr(stat, key), unit=key.endswith('s') and key.rsplit('_')[-1] or 's'\n",
" ) if 'time' in key else getattr(stat, key)\n",
" for key in dir(stat) if not key.startswith('_') and not callable(getattr(stat, key))})\n",
" # Append the change in time to the dataframe.\n",
" return DataFrame(stats, index).pipe(lambda df: df.join((df.st_mtime - df.st_birthtime).rename('dt')))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Control Flow in Procedural Notebooks\n",
"\n",
"A procedural notebooks will use clues from a namespace to decide what statements to execute in different contexts."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"if __name__ != '__main__': assert __name__+'.py' == __file__"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"### In Jupyter mode\n",
"\n",
"> **`__name__`** == **`'__main__'`**, but nothing is known about the python object **`__file__`**.\n",
"\n",
"### In Setup mode\n",
"\n",
"> **`__name__`** == **`'__main__'`** and **`assert __file__`** .\n",
"\n",
"### As a python package mode\n",
"\n",
"> **`__name__ + '.py'`** == **`__file__`**.\n",
"\n",
"### `get_ipython`\n",
"\n",
"The `get_ipython` context must be manually imported to use magics in converted notebooks."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
" from IPython import get_ipython"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Controlling value assignment\n",
"\n",
"Introspect the interactive Jupyter namespace to control expressions in procedural notebooks.\n",
"\n",
" \n",
" thing = get_ipython().user_ns.get('thing', 42):"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## `readme` procedures to makeparticles
\n",
"\n",
"Below are the procedures to test and create the `particles` package.\n",
"\n",
"* [`doctest`](https://docs.python.org/3/library/doctest.html)s were declared in each of our functions. `doctest` can be run in an interactive notebook session, [`unittest`](https://docs.python.org/3/library/unittest.html) cannot.\n",
"\n",
" `doctest` catches a lot of errors when it is in the _Restart and Run All_ pipeline. It is a great place to stash repeatedly typed statements.\n",
" \n",
"* When the tests pass write the __particles.ipynb__ notebook."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TestResults(failed=0, attempted=5)\n"
]
}
],
"source": [
"if __name__ == '__main__':\n",
" print(__import__('doctest').testmod())\n",
" Path(nb).write_text(__import__('nbformat').writes(particles))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"* Transform both __readme.ipynb__ and the newly minted __particles.ipynb__ to python scripts.\n",
"* Autopep it because we can.\n",
"* Rerun the same tests on __particles.py__"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[NbConvertApp] Converting notebook particles.ipynb to python\n",
"[NbConvertApp] Writing 1234 bytes to particles.py\n",
"[NbConvertApp] Converting notebook readme.ipynb to python\n",
"[NbConvertApp] Writing 10488 bytes to readme.py\n",
"success\n",
"[NbConvertApp] Converting notebook readme.ipynb to markdown\n",
"[NbConvertApp] Writing 10407 bytes to readme.md\n"
]
}
],
"source": [
"if __name__ == '__main__' and '__file__' not in globals():\n",
" !jupyter nbconvert --to python --TemplateExporter.exclude_input_prompt=True particles.ipynb readme.ipynb\n",
" !autopep8 --in-place --aggressive readme.py particles.ipynb\n",
" !python -m doctest particles.py & echo \"success\"\n",
" !jupyter nbconvert --to markdown --TemplateExporter.exclude_input_prompt=True readme.ipynb"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"* `setuptools` will install the __particles__ package using the conditions for setup mode. \n",
"\n",
" > Install the __particles__ package\n",
" \n",
" > `python readme.py develop`"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
" if __name__ == '__main__' and '__file__' in globals():\n",
" __import__('setuptools').setup(\n",
" name=\"particles\", \n",
" py_modules=['particles'], \n",
" install_requires=['notebook', 'pandas'])"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Reusing `particles`\n",
"\n",
"> A notebook that can be imported is reusable.\n",
"\n",
"__Particles__ can now be imported into the current scope. __particles__ allow the user to explore notebooks and their cells as data."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n","
"\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"\n",
"\n",
" \n",
" \n",
" \n",
" cell_type\n",
" execution_count\n",
" metadata\n",
" outputs\n",
" source\n",
" \n",
" \n",
" \n",
" \n",
" readme.ipynb\n",
" code\n",
" NaN\n",
" {}\n",
" []\n",
" if __name__ == '__main__':\\n print(__import...\n",
" \n",
" \n",
" readme.ipynb\n",
" markdown\n",
" NaN\n",
" {'slideshow': {'slide_type': '-'}}\n",
" NaN\n",
" ### In Jupyter mode\\n\\n> **`__name__`** == **`...\n",
" \n",
" \n",
" readme.ipynb\n",
" code\n",
" NaN\n",
" {'collapsed': True}\n",
" []\n",
" from IPython import get_ipython\n",
" \n",
" \n",
" particles.ipynb\n",
" code\n",
" NaN\n",
" {}\n",
" []\n",
" def files_to_data(df:DataFrame)->DataFrame:\\n ...\n",
" \n",
" \n",
" readme.ipynb\n",
" markdown\n",
" NaN\n",
" {}\n",
" NaN\n",
" > __particles__ is inspired by the New York T...\n",
" \n",
" \n",
"\n",
"
],
"text/plain": [
" cell_type execution_count \\\n",
"readme.ipynb code NaN \n",
"readme.ipynb markdown NaN \n",
"readme.ipynb code NaN \n",
"particles.ipynb code NaN \n",
"readme.ipynb markdown NaN \n",
"\n",
" metadata outputs \\\n",
"readme.ipynb {} [] \n",
"readme.ipynb {'slideshow': {'slide_type': '-'}} NaN \n",
"readme.ipynb {'collapsed': True} [] \n",
"particles.ipynb {} [] \n",
"readme.ipynb {} NaN \n",
"\n",
" source \n",
"readme.ipynb if __name__ == '__main__':\\n print(__import... \n",
"readme.ipynb ### In Jupyter mode\\n\\n> **`__name__`** == **`... \n",
"readme.ipynb from IPython import get_ipython \n",
"particles.ipynb def files_to_data(df:DataFrame)->DataFrame:\\n ... \n",
"readme.ipynb > __particles__ is inspired by the New York T... "
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import particles\n",
"assert particles.__file__.endswith('.py')\n",
"%matplotlib inline\n",
"from matplotlib import pyplot as plt\n",
"df = particles.read_notebooks()\n",
"df.sample(5)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"## Quantifying lines of code"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n","
"\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead tr th {\n",
" text-align: left;\n",
" }\n",
"\n",
"\n",
" \n",
" \n",
" \n",
" lines of ...\n",
" \n",
" \n",
" cell_type\n",
" code\n",
" markdown\n",
" \n",
" \n",
" \n",
" \n",
" particles.ipynb\n",
" 26.0\n",
" NaN\n",
" \n",
" \n",
" readme.ipynb\n",
" 66.0\n",
" 108.0\n",
" \n",
" \n",
"\n",
"
],
"text/plain": [
" lines of ... \n",
"cell_type code markdown\n",
"particles.ipynb 26.0 NaN\n",
"readme.ipynb 66.0 108.0"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.source.str.split('\\n').apply(len).groupby([df.index, df.cell_type]).sum().to_frame('lines of ...').unstack(-1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### The distribution of markdown and code cells in the __particles__ project.\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
" \n",
" df.cell_type.groupby(df.index).value_counts().unstack('cell_type').apply(lambda df: df.plot.pie() and plt.show());"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Summary\n",
"\n",
"This document must _Restart and Run All_ to acheive the goals of creating the __particles__ module.\n",
"\n",
"* __Procedural__ notebooks _Restart and Run All_ or they don't; they can be tested.\n",
"* Not all notebooks survive, the lucky ones become __procedural__ notebooks.\n",
"* Literate __procedural__ notebooks reinforce readability and reusability to reproducible work.\n",
"* __Procedural__ tend to maintain a longer shelf life than an exploratory notebook.\n",
"* __Interactive__ programming is complex and an author will rely on multiple styles of programming to acheive a __procedural__ document."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"celltoolbar": "Slideshow",
"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.5.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}